Come testate i metodi privati?


479

Sto costruendo una biblioteca di classe che avrà alcuni metodi pubblici e privati. Voglio essere in grado di testare unitamente i metodi privati ​​(principalmente durante lo sviluppo, ma potrebbe anche essere utile per il futuro refactoring).

Qual è il modo corretto per farlo?


3
Potrei mancare qualcosa, o forse è solo che questa domanda è, beh ... pre-historicin termini di anni di Internet, ma i test unitari di metodi privati ​​sono ora sia facili che diretti, con Visual Studio che produce le classi di accessori necessarie quando necessario e pre-riempire la logica dei test con frammenti dannatamente vicini a ciò che si potrebbe desiderare per semplici test funzionali. Vedi per es. msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspx
MJV

3
Questo sembra quasi un duplicato di stackoverflow.com/questions/34571/… .
Raedwald,

L'interrogante potrebbe non utilizzare Visual Studio
Dave il


Risposte:


122

Se si utilizza .net, è necessario utilizzare InternalsVisibleToAttribute .


86
Che schifo. Questo viene compilato nei tuoi assembly rilasciati.
Jay,

14
@Jay: non è possibile utilizzare #if DEBUGl' InternalsVisibleToattributo per renderlo non applicabile al codice di rilascio?
mpontillo,

20
@Mike, potresti, ma poi puoi solo eseguire unit test sul codice di debug, non sul codice di rilascio. Poiché il codice di rilascio è ottimizzato, è possibile che vengano visualizzati comportamenti e tempistiche diverse. Nel codice multithread ciò significa che i test delle unità non rileveranno in modo appropriato le condizioni di gara. Molto meglio è usare la riflessione tramite il suggerimento di @ AmazedSaint di seguito o usare il PrivateObject / PrivateType integrato. Ciò ti consente di vedere i privati ​​nelle build di rilascio supponendo che il tuo cablaggio di test sia in esecuzione con piena fiducia (cosa che MSTest esegue localmente)
Jay,

121
Cosa mi sto perdendo? Perché questa sarebbe la risposta accettata quando in realtà non risponde alla domanda specifica del test dei metodi privati? InternalsVisibleTo espone solo i metodi contrassegnati come interni e non quelli contrassegnati come privati ​​come richiesto dal PO (e il motivo per cui sono arrivato qui). Immagino di dover continuare a utilizzare PrivateObject come risposto da Seven?
Darren Lewis,

6
@Jay So che questo è un po 'in ritardo, ma un'opzione è quella di utilizzare qualcosa di simile #if RELEASE_TESTa InternalsVisibleToquello suggerito da Mike, e fare una copia della configurazione di build di rilascio che definisce RELEASE_TEST. È possibile testare il codice di rilascio con ottimizzazioni, ma quando si crea effettivamente per il rilascio, i test verranno omessi.
Shaz,

349

Se si desidera testare l'unità di un metodo privato, è possibile che qualcosa non vada. I test unitari sono (generalmente parlando) intesi a testare l'interfaccia di una classe, ovvero i suoi metodi pubblici (e protetti). Ovviamente puoi "hackerare" una soluzione a questo (anche solo rendendo pubblici i metodi), ma potresti anche considerare:

  1. Se vale davvero la pena provare il metodo che desideri testare, può valerne la pena spostarlo nella sua classe.
  2. Aggiungi altri test ai metodi pubblici che chiamano il metodo privato, testando la funzionalità del metodo privato. (Come indicato dai commentatori, dovresti farlo solo se la funzionalità di questi metodi privati ​​fa davvero parte dell'interfaccia pubblica. Se effettivamente eseguono funzioni che sono nascoste all'utente (cioè il test unitario), probabilmente è un male).

38
L'opzione 2 fa sì che i test unitari debbano avere conoscenza dell'implementazione sottostante della funzione. Non mi piace farlo. In genere penso che i test unitari dovrebbero testare la funzione senza assumere nulla sull'implementazione.
Herms,

15
Lo svantaggio dell'implementazione dei test è che i test saranno fragili da interrompere se si introducono modifiche all'implementazione. E questo è indesiderabile in quanto il refactoring è importante quanto scrivere i test in TDD.
JtR

30
Bene, i test dovrebbero rompersi se si modifica l'implementazione. TDD significherebbe cambiare prima i test.
sleske,

39
@sleske - Non sono del tutto d'accordo. Se la funzionalità non è cambiata, allora non c'è motivo per cui il test dovrebbe interrompersi, dal momento che i test dovrebbero davvero testare il comportamento / stato, non l'implementazione. Questo è ciò che significava jtr che rendeva fragili i tuoi test. In un mondo ideale, dovresti essere in grado di refactoring del codice e far passare ancora i test, verificando che il refactoring non abbia modificato la funzionalità del tuo sistema.
Alconja,

26
Ci scusiamo per l'ingenuità (non ancora molta esperienza con i test), ma non è l'idea di unit test per testare ogni modulo di codice da solo? Non capisco davvero perché i metodi privati ​​debbano essere esclusi da questa idea.
OMill

118

Potrebbe non essere utile testare metodi privati. Tuttavia, a volte mi piace anche chiamare metodi privati ​​da metodi di prova. Il più delle volte al fine di prevenire la duplicazione del codice per la generazione di dati di test ...

Microsoft fornisce due meccanismi per questo:

di accesso

  • Vai al codice sorgente della definizione di classe
  • Fare clic con il tasto destro del mouse sul nome della classe
  • Scegli "Crea accesso privato"
  • Scegli il progetto in cui deve essere creato l'accessor = = Finirai con una nuova classe con il nome foo_accessor. Questa classe verrà generata dinamicamente durante la compilazione e premi tutti i membri disponibili al pubblico.

Tuttavia, il meccanismo a volte è un po 'intrattabile quando si tratta di cambiamenti dell'interfaccia della classe originale. Quindi, la maggior parte delle volte evito di usarlo.

Classe PrivateObject L'altro modo è utilizzare Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject

// Wrap an already existing instance
PrivateObject accessor = new PrivateObject( objectInstanceToBeWrapped );

// Retrieve a private field
MyReturnType accessiblePrivateField = (MyReturnType) accessor.GetField( "privateFieldName" );

// Call a private method
accessor.Invoke( "PrivateMethodName", new Object[] {/* ... */} );

2
Come si invocano metodi statici privati?
Stuper:

18
Gli accessor privati ​​sono obsoleti in Visual Studio 2012 .
Ryan Gates,

3
Il metodo accessor per testare i metodi privati ​​è stato deprecato da VS 2011 in poi. blogs.msdn.com/b/visualstudioalm/archive/2012/03/08/…
Sanjit Misra

1
Leggendo i documenti trovati sul sito Web di Microsoft qui non vedo alcuna menzione della classe PrivateObject essere deprecata. Sto usando MSVS 2013 e funziona come previsto.
Stackunderflow

3
@RyanGates La prima soluzione sul sito a cui si fa riferimento afferma che la soluzione per accedere ai membri privati ​​è "Utilizzare la classe PrivateObject per facilitare l'accesso alle API interne e private nel codice. Questo si trova in Microsoft.VisualStudio.QualityTools.UnitTestFramework. dll assembly ".
Stackunderflow

78

Non sono d'accordo con la filosofia "dovresti essere interessato solo a testare l'interfaccia esterna". È un po 'come dire che un'officina riparazioni auto dovrebbe avere solo test per vedere se le ruote girano. Sì, alla fine sono interessato al comportamento esterno, ma mi piace che i miei test interni, privati, siano un po 'più specifici e pertinenti. Sì, se refactoring, potrei dover cambiare alcuni dei test, ma a meno che non sia un refactor massiccio, dovrò solo cambiarne alcuni e il fatto che gli altri test interni (invariati) funzionino ancora è un ottimo indicatore che il refactoring ha avuto successo.

Puoi provare a coprire tutti i casi interni utilizzando solo l'interfaccia pubblica e teoricamente è possibile testare tutti i metodi interni (o almeno quelli che contano) interamente utilizzando l'interfaccia pubblica ma potresti dover finire in piedi sulla testa per raggiungere questo e la connessione tra i casi di test eseguiti attraverso l'interfaccia pubblica e la parte interna della soluzione che sono progettati per testare può essere difficile o impossibile da discernere. Dopo aver sottolineato, i singoli test che garantiscono che il macchinario interno funzioni correttamente valgono le modifiche minori apportate al refactoring, almeno questa è stata la mia esperienza. Se devi apportare enormi cambiamenti ai tuoi test per ogni refactoring, allora forse questo non ha senso, ma in quel caso, forse dovresti ripensare completamente il tuo design.


20
Temo di non essere ancora d'accordo con te. Considerando ogni componente come una scatola nera, i moduli possono essere scambiati dentro / fuori senza problemi. Se hai una FooServicecosa da fare X, tutto ciò di cui dovresti preoccuparti è che lo fa davvero Xquando richiesto. Come lo fa non dovrebbe importare. Se ci sono problemi nella classe non rilevabili attraverso l'interfaccia (improbabile), è ancora valido FooService. Se si tratta di un problema che è visibile attraverso l'interfaccia, un test su membri pubblici dovrebbe rilevarlo. Il punto dovrebbe essere che fintanto che la ruota gira correttamente, può essere utilizzata come ruota.
Base

5
Un approccio generale è che se la tua logica interna è abbastanza complicata da ritenere che richieda test unitari, forse deve essere estratta in una sorta di classe di supporto con un'interfaccia pubblica che può essere testata dall'unità. Quindi la tua classe 'genitore' può semplicemente fare uso di questo aiutante e tutti possono essere testati in modo appropriato.
Mir

9
@Basic: logica completamente sbagliata in questa risposta. Il caso classico quando è necessario un metodo privato è quando è necessario riutilizzare del codice con metodi pubblici. Hai inserito questo codice in alcuni PrivMethod. Questo metodo non deve essere esposto al pubblico, ma deve essere testato per garantire che i metodi pubblici, che si basano su PrivMethod, possano davvero fare affidamento su di esso.
Dima,

6
@Dima Sicuramente allora se c'è un problema PrivMethod, un test su PubMethodquali chiamate PrivMethoddovrebbero esporlo? Cosa succede quando cambi il tuo SimpleSmtpServicein a GmailService? All'improvviso i tuoi test privati ​​indicano il codice che non esiste più o forse funziona diversamente e fallirebbe, anche se l'applicazione potrebbe funzionare perfettamente come previsto. Se esiste un'elaborazione complessa che si applicherebbe a entrambi i mittenti di e-mail, forse dovrebbe essere in un modo EmailProcessorche può essere utilizzato da entrambi e testato separatamente?
Base

2
@miltonb Potremmo vederlo da diversi stili di sviluppo. WRT gli interni, non tendo a testarli. Se c'è un problema (come identificato dai test di interfaccia), è facile rintracciare collegando un debugger o la classe è troppo complessa e dovrebbe essere suddivisa (con l'interfaccia pubblica della nuova unità di classi testata) IMHO
Basic

51

In rari casi ho voluto testare funzioni private, di solito le ho modificate per proteggerle e ho scritto una sottoclasse con una funzione wrapper pubblica.

La classe:

...

protected void APrivateFunction()
{
    ...
}

...

Sottoclasse per test:

...

[Test]
public void TestAPrivateFunction()
{
    APrivateFunction();
    //or whatever testing code you want here
}

...

1
Puoi persino inserire quella classe figlio nel tuo file di test unitario invece di ingombrare la classe reale. +1 per astuzia.
Tim Abell,

Se possibile, inserisco sempre tutto il codice relativo al test nel progetto unit test. Questo era solo psuedo-codice.
Jason Jackson,

2
Questa funzione non è privata, è protetta, il risultato netto ... hai reso il tuo codice meno sicuro / esposto a funzionalità private di tipo figlio
Guerra

22

Penso che una domanda più fondamentale che dovrebbe essere posta è che perché stai cercando di testare il metodo privato in primo luogo. Questo è un odore di codice che stai provando a testare il metodo privato attraverso l'interfaccia pubblica di quella classe mentre quel metodo è privato per una ragione in quanto è un dettaglio di implementazione. Bisogna preoccuparsi solo del comportamento dell'interfaccia pubblica e non di come viene implementato sotto le coperte.

Se voglio testare il comportamento del metodo privato, usando i refactoring comuni, posso estrarre il suo codice in un'altra classe (forse con visibilità a livello di pacchetto, quindi assicurati che non faccia parte di un'API pubblica). Posso quindi testare il suo comportamento da solo.

Il prodotto del refactoring significa che il metodo privato è ora una classe separata che è diventata un collaboratore della classe originale. Il suo comportamento sarà stato ben compreso attraverso i propri test unitari.

Posso quindi deridere il suo comportamento quando provo a testare la classe originale in modo da potermi concentrare sul test del comportamento dell'interfaccia pubblica di quella classe piuttosto che dover testare un'esplosione combinatoria dell'interfaccia pubblica e il comportamento di tutti i suoi metodi privati .

Lo vedo analogo alla guida di un'auto. Quando guido una macchina, non guido con il cofano rivolto verso l'alto, così posso vedere che il motore funziona. Mi affido all'interfaccia fornita dall'auto, vale a dire il contagiri e il tachimetro per sapere che il motore funziona. Mi affido al fatto che l'auto si muove effettivamente quando premo il pedale del gas. Se voglio testare il motore posso fare controlli su questo in modo isolato. : D

Naturalmente testare direttamente i metodi privati ​​può essere l'ultima risorsa se si dispone di un'applicazione legacy, ma preferirei che il codice legacy venga sottoposto a refactoring per consentire test migliori. Michael Feathers ha scritto un ottimo libro su questo argomento. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052


16
Logica completamente sbagliata in questa risposta. Il caso classico quando è necessario un metodo privato è quando è necessario riutilizzare del codice con metodi pubblici. Hai inserito questo codice in alcuni PrivMethod. Questo metodo non deve essere esposto al pubblico, ma deve essere testato per garantire che i metodi pubblici, che si basano su PrivMethod, possano davvero fare affidamento su di esso.
Dima,

Ha senso durante lo sviluppo iniziale, ma vuoi test per metodi privati ​​nella tua tuta di regressione standard? In tal caso, se l'implementazione cambia, può interrompere la suite di test. OTOH, se i test di regressione si concentrano solo su metodi pubblici visibili esternamente, quindi se il metodo privato si interrompe in seguito, la suite di regressione dovrebbe comunque rilevare l'errore. Quindi, se necessario, è possibile rispolverare il vecchio test privato, se necessario.
Alex Blakemore,

9
In disaccordo, dovresti testare solo l'interfaccia pubblica, altrimenti perché hai bisogno di metodi privati. Renderli tutti pubblici in quel caso e testarli tutti. Se stai testando metodi privati ​​stai rompendo l'enscapulation. Se vuoi testare un metodo privato ed è usato in più metodi pubblici, allora dovrebbe essere spostato nella sua stessa classe e testato in isolamento. Tutti i metodi pubblici dovrebbero quindi delegare a quella nuova classe. In questo modo hai ancora test per interfaccia della classe originale e puoi verificare che il comportamento non sia cambiato e hai test separati per il metodo privato delegato.
Big Kahuna,

@Big Kahuna - Se pensi che non ci siano casi in cui devi testare unitamente metodi privati, non hai mai lavorato con un progetto abbastanza grande / complesso. Molte volte una funzione pubblica come le convalide specifiche del client finiscono con 20 righe che chiamano semplicemente metodi privati ​​molto semplici per rendere il codice più leggibile, ma è comunque necessario testare ogni singolo metodo privato. Testare 20 volte la funzione pubblica renderà molto difficile il debutto quando i test unitari falliscono.
Pedro.The.Kid

1
Lavoro per un'azienda FTSE 100. Penso di aver visto diversi progetti complessi ai miei tempi, grazie. Se devi testare a quel livello, allora ogni metodo privato come collaboratori separati, dovrebbe essere testato separatamente perché implica che hanno un comportamento individuale che necessita di test. Il test per l'oggetto mediatore principale diventa quindi solo un test di interazione. Verifica solo che venga chiamata la strategia corretta. Il tuo scenario sembra che la classe in questione non stia seguendo SRP. Non ha un solo motivo per cambiare ma 20 => violazione SRP. Leggi il libro GOOS o lo zio Bob. YMWV
Big Kahuna,

17

I tipi privati, interni e membri privati ​​sono così a causa di qualche motivo, e spesso non vuoi fare confusione con loro direttamente. E se lo fai, è probabile che ti romperai più tardi, perché non vi è alcuna garanzia che i ragazzi che hanno creato tali assemblee manterranno le implementazioni private / interne in quanto tali.

Ma, a volte, quando faccio alcuni hack / esplorazioni di assiemi compilati o di terze parti, mi sono ritrovato a voler inizializzare una classe privata o una classe con un costruttore privato o interno. O, a volte, quando ho a che fare con librerie pre-compilate che non posso cambiare - finisco per scrivere alcuni test con un metodo privato.

Nasce così AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - è una classe di wrapper rapido che semplifica il lavoro utilizzando le funzionalità dinamiche e la riflessione di C # 4.0.

Puoi creare tipi interni / privati ​​come

    //Note that the wrapper is dynamic
    dynamic wrapper = AccessPrivateWrapper.FromType
        (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor");

    //Access the private members
    wrapper.PrivateMethodInPrivateClass();

12

Bene, puoi testare il metodo privato in due modi

  1. è possibile creare un'istanza di PrivateObjectclasse la sintassi è la seguente

    PrivateObject obj= new PrivateObject(PrivateClass);
    //now with this obj you can call the private method of PrivateCalss.
    obj.PrivateMethod("Parameters");
  2. Puoi usare la riflessione.

    PrivateClass obj = new PrivateClass(); // Class containing private obj
    Type t = typeof(PrivateClass); 
    var x = t.InvokeMember("PrivateFunc", 
        BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Public |  
            BindingFlags.Instance, null, obj, new object[] { 5 });

Buona risposta, ma per la prima sintassi la tua sintassi è sbagliata. Devi dichiarare PrivateClassprima un'istanza e usarla. stackoverflow.com/questions/9122708/...
SharpC

10

Ho anche usato il metodo InternalsVisibleToAttribute. Vale anche la pena ricordare che, se ti senti a disagio nel rendere interni i tuoi metodi precedentemente privati ​​per raggiungere questo obiettivo, forse non dovrebbero comunque essere oggetto di test unitari diretti.

Dopotutto, stai testando il comportamento della tua classe, piuttosto che la sua implementazione specifica : puoi cambiare quest'ultima senza cambiare la prima e i tuoi test dovrebbero comunque passare.


Adoro il punto di testare il comportamento piuttosto che l'implementazione. Se colleghi i test unitari all'implementazione (metodi privati), i test diventeranno fragili e dovranno cambiare quando l'implementazione cambia.
Bob Horn,

9

Esistono 2 tipi di metodi privati. Metodi privati ​​statici e metodi privati ​​non statici (metodi di istanza). I seguenti 2 articoli spiegano come testare i metodi privati ​​con esempi.

  1. Test unitari Metodi privati ​​statici
  2. Test unitari Metodi privati ​​non statici

Fornisci alcuni esempi, non solo fornisci un link
vinculis,

Sembra brutto. No intellisense. Cattiva soluzione dalla SM. Sono sotto shock!
alerya,

Il modo più semplice per testare metodi statici privati
Concedi

8

MS Test ha una bella funzionalità integrata che rende disponibili membri e metodi privati ​​nel progetto creando un file chiamato VSCodeGenAccessors

[System.Diagnostics.DebuggerStepThrough()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
    internal class BaseAccessor
    {

        protected Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject m_privateObject;

        protected BaseAccessor(object target, Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
        {
            m_privateObject = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(target, type);
        }

        protected BaseAccessor(Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType type)
            :
                this(null, type)
        {
        }

        internal virtual object Target
        {
            get
            {
                return m_privateObject.Target;
            }
        }

        public override string ToString()
        {
            return this.Target.ToString();
        }

        public override bool Equals(object obj)
        {
            if (typeof(BaseAccessor).IsInstanceOfType(obj))
            {
                obj = ((BaseAccessor)(obj)).Target;
            }
            return this.Target.Equals(obj);
        }

        public override int GetHashCode()
        {
            return this.Target.GetHashCode();
        }
    }

Con classi che derivano da BaseAccessor

ad esempio

[System.Diagnostics.DebuggerStepThrough()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TestTools.UnitTestGeneration", "1.0.0.0")]
internal class SomeClassAccessor : BaseAccessor
{

    protected static Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType m_privateType = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateType(typeof(global::Namespace.SomeClass));

    internal SomeClassAccessor(global::Namespace.Someclass target)
        : base(target, m_privateType)
    {
    }

    internal static string STATIC_STRING
    {
        get
        {
            string ret = ((string)(m_privateType.GetStaticField("STATIC_STRING")));
            return ret;
        }
        set
        {
            m_privateType.SetStaticField("STATIC_STRING", value);
        }
    }

    internal int memberVar    {
        get
        {
            int ret = ((int)(m_privateObject.GetField("memberVar")));
            return ret;
        }
        set
        {
            m_privateObject.SetField("memberVar", value);
        }
    }

    internal int PrivateMethodName(int paramName)
    {
        object[] args = new object[] {
            paramName};
        int ret = (int)(m_privateObject.Invoke("PrivateMethodName", new System.Type[] {
                typeof(int)}, args)));
        return ret;
    }

8
I file generati esistono solo in VS2005. Nel 2008 sono generati dietro le quinte. E sono un abmomination. E l'attività Shadow associata è instabile su un server di build.
Ruben Bartelink,

Anche gli accessori sono stati deprecati in VS2012-2013.
Zephan Schroeder

5

Su CodeProject, c'è un articolo che discute brevemente pro e contro del test di metodi privati. Fornisce quindi un codice di riflessione per accedere ai metodi privati ​​(simile al codice fornito da Marcus sopra). L'unico problema che ho riscontrato con l'esempio è che il codice non tiene conto dei metodi sovraccarichi.

Puoi trovare l'articolo qui:

http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx



4

Tendo a non usare le direttive del compilatore perché ingombrano rapidamente le cose. Un modo per mitigarlo se ne hai davvero bisogno è metterli in una classe parziale e fare in modo che la tua build ignori quel file .cs quando crei la versione di produzione.


Includeresti gli accessori di test in una versione di produzione (per testare le ottimizzazioni del compilatore ecc.) Ma li escluderesti in una versione di rilascio. Ma sto spaccando i capelli e l'ho votato comunque perché penso che sia una buona idea mettere quella roba in un unico posto. Grazie per l'idea
Bloke CAD,

4

Non dovresti testare i metodi privati ​​del tuo codice in primo luogo. Dovresti testare l '"interfaccia pubblica" o l'API, le cose pubbliche delle tue classi. Le API sono tutti i metodi pubblici che esponi a chiamanti esterni.

Il motivo è che una volta che inizi a testare i metodi privati ​​e gli interni della tua classe, stai accoppiando l'implementazione della tua classe (le cose private) ai tuoi test. Ciò significa che quando si decide di modificare i dettagli dell'implementazione, è necessario modificare anche i test.

Per questo motivo dovresti evitare di usare InternalsVisibleToAtrribute.

Ecco un grande discorso di Ian Cooper che tratta questo argomento: Ian Cooper: TDD, dove è andato tutto storto


3

A volte, può essere utile testare dichiarazioni private. Fondamentalmente, un compilatore ha un solo metodo pubblico: Compile (string outputFileName, params string [] sourceSFileNames). Sono sicuro che capirai che sarebbe difficile testare un tale metodo senza testare ogni dichiarazione "nascosta"!

Ecco perché abbiamo creato Visual T #: per rendere più semplici i test. È un linguaggio di programmazione .NET gratuito (compatibile con C # v2.0).

Abbiamo aggiunto l'operatore '.-'. Si comporta semplicemente come '.' operatore, tranne che puoi anche accedere a qualsiasi dichiarazione nascosta dai tuoi test senza modificare nulla nel tuo progetto testato.

Date un'occhiata al nostro sito web: scaricare è gratis .


3

Sono sorpreso che nessuno l'abbia ancora detto, ma una soluzione che ho usato è quella di creare un metodo statico all'interno della classe per testarlo. Questo ti dà accesso a tutto ciò che è pubblico e privato con cui provare.

Inoltre, in un linguaggio di scripting (con capacità OO, come Python, Ruby e PHP), è possibile eseguire il test del file stesso quando eseguito. Bel modo rapido per assicurarsi che le modifiche non abbiano interrotto nulla. Questo ovviamente rende una soluzione scalabile per testare tutte le tue classi: eseguile tutte. (puoi anche farlo in altre lingue con un vuoto principale che esegue sempre anche i suoi test).


1
Sebbene sia pratico, non è molto elegante. Questo può creare un po 'di confusione di una base di codice e inoltre non ti consente di separare i tuoi test dal tuo codice reale. La possibilità di testare esternamente offre la possibilità di eseguire lo script per automatizzare i test invece di scrivere manualmente metodi statici.
Darren Reid,

Questo non ti impedisce di testare esternamente ... basta chiamare il metodo statico come preferisci. Anche la base di codice non è disordinata ... tu assegni il metodo di conseguenza. Uso "runTest", ma funziona in modo simile.
rube

Il tuo diritto, non preclude il test esterno, tuttavia genera molto più codice, cioè rende la base di codice disordinata. Ogni classe potrebbe avere molti metodi privati ​​per testare che inizializza le sue variabili in uno o più dei suoi costruttori. Per eseguire il test, dovrai scrivere almeno tutti i metodi statici quanti sono i metodi da testare e potrebbe essere necessario che i metodi di test siano grandi per inizializzare i valori corretti. Ciò renderebbe più difficile la manutenzione del codice. Come altri hanno già detto, testare il comportamento di una classe è un approccio migliore, il resto dovrebbe essere abbastanza piccolo da eseguire il debug.
Darren Reid,

Uso lo stesso numero di righe per testare chiunque altro (in realtà meno di quanto leggerai più avanti). Non devi testare TUTTI i tuoi metodi privati. Solo quelli che hanno bisogno di essere testati :) Inoltre, non è necessario testarli ciascuno con un metodo separato. Lo faccio con una sola chiamata. Questo in realtà rende difficile la manutenzione del codice MENO, dal momento che tutte le mie classi hanno lo stesso metodo di test dell'unità ombrello, che esegue tutti i test delle unità private e protette riga per riga. L'intero cablaggio di prova chiama quindi lo stesso metodo su tutte le mie classi e la manutenzione risiede all'interno della mia classe - prove e tutto.
rube

3

Voglio creare qui un chiaro esempio di codice che puoi usare su qualsiasi classe in cui vuoi testare il metodo privato.

Nella classe del tuo test case includi semplicemente questi metodi e poi impiegali come indicato.

  /**
   *
   * @var Class_name_of_class_you_want_to_test_private_methods_in
   * note: the actual class and the private variable to store the 
   * class instance in, should at least be different case so that
   * they do not get confused in the code.  Here the class name is
   * is upper case while the private instance variable is all lower
   * case
   */
  private $class_name_of_class_you_want_to_test_private_methods_in;

  /**
   * This uses reflection to be able to get private methods to test
   * @param $methodName
   * @return ReflectionMethod
   */
  protected static function getMethod($methodName) {
    $class = new ReflectionClass('Class_name_of_class_you_want_to_test_private_methods_in');
    $method = $class->getMethod($methodName);
    $method->setAccessible(true);
    return $method;
  }

  /**
   * Uses reflection class to call private methods and get return values.
   * @param $methodName
   * @param array $params
   * @return mixed
   *
   * usage:     $this->_callMethod('_someFunctionName', array(param1,param2,param3));
   *  {params are in
   *   order in which they appear in the function declaration}
   */
  protected function _callMethod($methodName, $params=array()) {
    $method = self::getMethod($methodName);
    return $method->invokeArgs($this->class_name_of_class_you_want_to_test_private_methods_in, $params);
  }

$ this -> _ callMethod ('_ someFunctionName', array (param1, param2, param3));

Emetti semplicemente i parametri nell'ordine in cui compaiono nella funzione privata originale


3

Per chiunque voglia eseguire metodi privati ​​senza tutti i problemi. Funziona con qualsiasi framework di unit test usando nient'altro che un buon vecchio Reflection.

public class ReflectionTools
{
    // If the class is non-static
    public static Object InvokePrivate(Object objectUnderTest, string method, params object[] args)
    {
        Type t = objectUnderTest.GetType();
        return t.InvokeMember(method,
            BindingFlags.InvokeMethod |
            BindingFlags.NonPublic |
            BindingFlags.Instance |
            BindingFlags.Static,
            null,
            objectUnderTest,
            args);
    }
    // if the class is static
    public static Object InvokePrivate(Type typeOfObjectUnderTest, string method, params object[] args)
    {
        MemberInfo[] members = typeOfObjectUnderTest.GetMembers(BindingFlags.NonPublic | BindingFlags.Static);
        foreach(var member in members)
        {
            if (member.Name == method)
            {
                return typeOfObjectUnderTest.InvokeMember(method, BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.InvokeMethod, null, typeOfObjectUnderTest, args);
            }
        }
        return null;
    }
}

Quindi nei tuoi test reali, puoi fare qualcosa del genere:

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    typeof(StaticClassOfMethod), 
    "PrivateMethod"), 
  "Expected Result");

Assert.AreEqual( 
  ReflectionTools.InvokePrivate(
    new ClassOfMethod(), 
    "PrivateMethod"), 
  "Expected Result");

2

MbUnit ha ottenuto un bel wrapper per questo chiamato Reflector.

Reflector dogReflector = new Reflector(new Dog());
dogReflector.Invoke("DreamAbout", DogDream.Food);

È inoltre possibile impostare e ottenere valori dalle proprietà

dogReflector.GetProperty("Age");

Per quanto riguarda il "test privato", sono d'accordo che .. nel mondo perfetto. non ha senso fare test di unità private. Ma nel mondo reale potresti voler voler scrivere test privati ​​invece di refactoring del codice.


4
Solo per informazioni, Reflectorè stato sostituito dal più potente Mirrorin Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )
Yann Trevin,

2

Ecco un buon articolo sul test unitario di metodi privati. Ma non sono sicuro di cosa sia meglio, per farti un'applicazione progettata appositamente per i test (è come creare test solo per i test) o usare la riflessione per i test. Abbastanza sicuro che molti di noi sceglieranno la seconda strada.


2

Secondo me dovresti solo testare l'API pubblica della tua classe.

Rendere pubblico un metodo, al fine di testarlo, rompe l'incapsulamento esponendo i dettagli dell'implementazione.

Una buona API pubblica risolve un obiettivo immediato del codice client e lo risolve completamente.


Questa dovrebbe essere la risposta giusta IMO. Se hai molti metodi privati, probabilmente è perché hai una classe nascosta che dovresti sfondare nella sua interfaccia pubblica.
Sunefred

2

Uso la classe PrivateObject . Ma come accennato in precedenza meglio evitare di testare metodi privati.

Class target = new Class();
PrivateObject obj = new PrivateObject(target);
var retVal = obj.Invoke("PrivateMethod");
Assert.AreEqual(retVal);

2
CC -Dprivate=public

"CC" è il compilatore della riga di comando sul sistema che utilizzo. -Dfoo=barfa l'equivalente di #define foo bar. Quindi, questa opzione di compilazione cambia efficacemente tutte le cose private in pubbliche.


2
Cos'è questo? Questo vale per Visual Studio?
Sì, il

"CC" è il compilatore della riga di comando sul sistema che utilizzo. "-Dfoo = bar" fa l'equivalente di "#define foo bar". Quindi, questa opzione di compilazione cambia efficacemente tutte le cose private in pubbliche. ha-ha!
Mark Harrison,

In Visual Studio, imposta una definizione nel tuo ambiente di generazione.
Mark Harrison,

1

Ecco un esempio, prima la firma del metodo:

private string[] SplitInternal()
{
    return Regex.Matches(Format, @"([^/\[\]]|\[[^]]*\])+")
                        .Cast<Match>()
                        .Select(m => m.Value)
                        .Where(s => !string.IsNullOrEmpty(s))
                        .ToArray();
}

Ecco il test:

/// <summary>
///A test for SplitInternal
///</summary>
[TestMethod()]
[DeploymentItem("Git XmlLib vs2008.dll")]
public void SplitInternalTest()
{
    string path = "pair[path/to/@Key={0}]/Items/Item[Name={1}]/Date";
    object[] values = new object[] { 2, "Martin" };
    XPathString xp = new XPathString(path, values);

    PrivateObject param0 = new PrivateObject(xp);
    XPathString_Accessor target = new XPathString_Accessor(param0);
    string[] expected = new string[] {
        "pair[path/to/@Key={0}]",
        "Items",
        "Item[Name={1}]",
        "Date"
    };
    string[] actual;
    actual = target.SplitInternal();
    CollectionAssert.AreEqual(expected, actual);
}

1

Un modo per farlo è quello di avere il tuo metodo protectede scrivere un dispositivo di prova che erediti la tua classe per essere testato. In questo modo, non stai né trasformando il tuo metodo public, ma attivi il test.


Non sono d'accordo con questo, perché consentirai anche ai tuoi consumatori di ereditare dalla classe di base e utilizzare le funzioni protette. Questo era qualcosa che volevi prevenire in primo luogo rendendo tali funzioni private o interne.
Nick N.

1

1) Se si dispone di un codice legacy, l'unico modo per testare metodi privati ​​è tramite la riflessione.

2) Se si tratta di un nuovo codice, hai le seguenti opzioni:

  • Usa la riflessione (per complicare)
  • Scrivi unit test nella stessa classe (rende il codice di produzione brutto avendo anche il codice di test)
  • Rifattorizzare e rendere pubblico il metodo in una sorta di classe util
  • Usa l'annotazione @VisibleForTesting e rimuovi privato

Preferisco il metodo di annotazione, più semplice e meno complicato. L'unico problema è che abbiamo aumentato la visibilità che penso non sia una grande preoccupazione. Dovremmo sempre codificare per interfacciare, quindi se abbiamo un'interfaccia MyService e un'implementazione MyServiceImpl, allora possiamo avere le classi di test corrispondenti che sono MyServiceTest (metodi dell'interfaccia di test) e MyServiceImplTest (metodi privati ​​di test). Tutti i client dovrebbero comunque utilizzare l'interfaccia in modo tale che, sebbene la visibilità del metodo privato sia stata aumentata, non dovrebbe importare.


1

Puoi anche dichiararlo come pubblico o interno (con InternalsVisibleToAttribute) durante la creazione in modalità debug:

    /// <summary>
    /// This Method is private.
    /// </summary>
#if DEBUG
    public
#else
    private
#endif
    static string MyPrivateMethod()
    {
        return "false";
    }

Gonfia il codice, ma sarà privatein una build di rilascio.


0

È possibile generare il metodo di test per il metodo privato da Visual Studio 2008. Quando si crea un test di unità per un metodo privato, una cartella Riferimenti test viene aggiunta al progetto di test e un accessor viene aggiunto a quella cartella. L'accessorio viene anche indicato nella logica del metodo di test dell'unità. Questo accessorio consente all'unità di test di chiamare metodi privati ​​nel codice che si sta testando. Per i dettagli dai un'occhiata

http://msdn.microsoft.com/en-us/library/bb385974.aspx


0

Si noti inoltre che InternalsVisibleToAtrribute richiede che l'assembly abbia un nome sicuro , il che crea il proprio insieme di problemi se si lavora in una soluzione che non aveva avuto quel requisito prima. Uso l'accessor per testare metodi privati. Vedi questa domanda che per un esempio di questo.


2
No, InternalsVisibleToAttributenon è necessario che i tuoi assiemi vengano nominati con forza. Attualmente lo uso su un progetto in cui non è così.
Cody Grey

1
Per chiarire questo: "Sia l'assembly corrente che l'assembly amico devono essere non firmati o entrambi devono essere firmati con un nome sicuro". - Da MSDN
Hari
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.