Rendere il codice interno ma disponibile per i test unitari di altri progetti


129

Mettiamo tutti i nostri test unitari nei loro progetti. Scopriamo che dobbiamo rendere pubbliche alcune classi anziché interne solo per i test unitari. C'è comunque modo di evitare di doverlo fare. Quali sono le implicazioni della memoria rendendo le classi pubbliche anziché sigillate?


Risposte:


205

Se si utilizza .NET, l' attributo assembly InternalsVisibleTo consente di creare assembly "amici". Si tratta di assiemi specifici con nome sicuro che possono accedere alle classi interne e ai membri dell'altro assembly.

Nota, questo dovrebbe essere usato con discrezione in quanto accoppia strettamente gli assiemi coinvolti. Un uso comune per InternalsVisibleTo è per i progetti di unit testing. Probabilmente non è una buona scelta per l'uso negli assiemi di applicazioni reali, per il motivo sopra indicato.

Esempio:

[assembly: InternalsVisibleTo("NameAssemblyYouWantToPermitAccess")]
namespace NameOfYourNameSpace
{

23
Suggerirei di mettere un DEBUG #if attorno all'attributo e quindi di testare l'unità nel debug. In questo modo sarai sicuro che l'attributo non è impostato sul codice di rilascio.
Steve Cook

Questa è solo un'idea, non lo so .... Che ne dici di: #if DEBUG classe pubblica IniReader #else classe interna IniReader #endif Probabilmente non raccomandato? Perché?
jmelhus,

4
Bene, perché limitare i test alle build di debug?
Marco Mp,

2
Inoltre, solo un pignolo, non è necessario che le assemblee degli "amici" siano chiamate in modo forte (potrebbe essere una buona pratica, anche se il mio gusto personale dice diversamente, ma non è obbligatorio).
Marco Mp,

9
Disaccordo. Se sto costruendo un componente complesso con un'API pubblica molto sottile, non è pratico e non realistico testare solo tramite l'API pubblica. Ti ritroverai con una palla di fango non mantenibile. Invece, definirei attentamente le unità interne e le testerei separatamente. Come ha detto abbastanza spesso Jeremy D. Miller: "Prova in piccolo prima di testare in grande".
Dennis Doomen,

6

Se si tratta di una classe interna, non deve essere utilizzata isolatamente. Pertanto non dovresti davvero provarlo a parte testare qualche altra classe che utilizza quell'oggetto internamente.

Proprio come non dovresti testare membri privati ​​di una classe, non dovresti testare classi interne di una DLL. Tali classi sono dettagli di implementazione di alcune classi accessibili al pubblico e pertanto dovrebbero essere ben esercitate attraverso altri test unitari.

L'idea è che si desidera solo testare il comportamento di una classe perché se si verificano i dettagli dell'implementazione interna, i test saranno fragili. Dovresti essere in grado di modificare i dettagli di implementazione di qualsiasi classe senza interrompere tutti i test.

Se scopri che hai davvero bisogno di testare quella classe, allora potresti voler riesaminare perché quella classe è interna in primo luogo.


2
I dettagli di attuazione dovrebbero essere esercitati come parte di un test globale. Non sbirciare le variabili private ... testare il comportamento previsto. Se il test è corretto .. tutti gli impianti idraulici e i cavi interni devono essere testati come parte di esso. Votato.
Gishu,

69
non sono necessariamente d'accordo con questo in quanto queste classi sono "pubbliche" ad altre classi all'interno della DLL e le funzionalità della classe dovrebbero essere testate in modo indipendente
leora,

27
Anche io non sono d'accordo. Le unità sono unità e devono essere testate separatamente.
Sentinella

5
Ottima linea guida generale, ma non diventiamo dogmatici. I test hanno due scopi principali: 1) Regressione: assicurati di non aver rotto qualcosa, 2) Aumentare la velocità di sviluppo. Se devo alzare un enorme servizio ogni volta che voglio scrivere una riga di codice, ostacolerò lo sviluppo. Se ho un pezzo interno complesso voglio essere in grado di sviluppare e testare da solo. Al contrario, non voglio che tutto venga testato in modo isolato (riducendo così il valore del test di regressione o duplicando il codice del test), ma alcuni codici giustificano l'isolamento. Forse la chiave è renderlo un assieme separato.
Lee Jensen,

2
OP qui è corretto. Stai accoppiando i tuoi test all'implementazione interna. Quindi la tua squadra è per sempre schiava di riparare i test e il codice di derisione orribile. Prova solo le API pubbliche, sia che siano le API lib impacchettate o le API esposte in rete. Tutto il codice dovrebbe essere esercitabile tramite la porta d'ingresso. Testare l'implementazione è il motivo per cui TDD è in gran parte morto, è solo un enorme PITA per testare letteralmente ogni classe dell'intera app, piuttosto che concentrarsi sull'assicurare comportamenti del sistema. Penso che quasi tutti, compresi i libri "autorevoli", abbiano avuto questo torto o non lo abbiano chiarito.
Luke Puplett,

4

a scopo di documentazione

in alternativa è possibile creare un'istanza della classe interna utilizzando il Type.GetTypemetodo

esempio

//IServiceWrapper is public class which is 
//the same assembly with the internal class 
var asm = typeof(IServiceWrapper).Assembly;
//Namespace.ServiceWrapper is internal
var type = asm.GetType("Namespace.ServiceWrapper");
return (IServiceWrapper<T>)Activator
    .CreateInstance(type, new object[1] { /*constructor parameter*/ });

per tipo generico ci sono diversi processi come qui sotto:

var asm = typeof(IServiceWrapper).Assembly;
//note the name Namespace.ServiceWrapper`1
//this is for calling Namespace.ServiceWrapper<>
var type = asm.GetType("Namespace.ServiceWrapper`1");
var genType = type.MakeGenericType(new Type[1] { typeof(T) });
return (IServiceWrapper<T>)Activator
     .CreateInstance(genType, new object[1] { /*constructor parameter*/});

-5

Le lezioni possono essere sia pubbliche che sigillate.

Ma non farlo.

È possibile creare uno strumento per riflettere sulle classi interne ed emettere una nuova classe che accede a tutto tramite la riflessione. MSTest lo fa.

Modifica: Voglio dire, se non si desidera includere roba di collaudo nell'assieme originale; questo funziona anche se i membri sono privati.


1
Aspetta cosa? Stai dicendo di non fare un public sealed class? Qual è il tuo ragionamento per quella gemma?
schiaccia il

@crush traumapony non si collega dal 2009.
Prof. Falken,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.