Come collaudi unitamente i metodi privati?

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

Qual è il modo corretto per farlo?

Se si utilizza .net, è necessario utilizzare InternalsVisibleToAttribute .

Se si desidera testare un metodo privato, qualcosa potrebbe essere sbagliato. I test unitari sono (in generale) destinati a testare l’interfaccia di una class, intendendo i suoi metodi pubblici (e protetti). Ovviamente puoi “hackerare” una soluzione (anche solo rendendo pubblici i metodi), ma potresti anche prendere in considerazione:

  1. Se vale davvero la pena provare il metodo che desideri testare, potrebbe valere la pena spostarlo nella sua class.
  2. Aggiungi altri test ai metodi pubblici che chiamano il metodo privato, testando le funzionalità del metodo privato. (Come indicato dai commentatori, dovresti farlo solo se la funzionalità di questi metodi privati ​​è davvero parte integrante dell’interfaccia pubblica. Se effettivamente eseguono funzioni che sono nascoste all’utente (cioè il test di unità), questo è probabilmente sbagliato).

Potrebbe non essere utile testare metodi privati. Tuttavia, a volte mi piace anche chiamare metodi privati ​​dai metodi di prova. La maggior parte delle volte al fine di prevenire la duplicazione del codice per la generazione dei dati di test …

Microsoft fornisce due meccanismi per questo:

di accesso

  • Vai al codice sorgente della definizione di class
  • Fai clic con il tasto destro sul nome della class
  • Scegli “Crea accesso privato”
  • Scegli il progetto in cui creare l’accessor => Avrai una nuova class con il nome foo_accessor. Questa class verrà generata dynamicmente durante la compilazione e prevede che tutti i membri siano disponibili al pubblico.

Tuttavia, il meccanismo a volte è un po ‘intrattabile quando si tratta di cambiamenti dell’interfaccia della class originale. Quindi, la maggior parte delle volte evito di usare questo.

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[] {/* ... */} ); 

Non sono d’accordo con la filosofia “dovresti solo essere interessato a testare l’interfaccia esterna”. È un po ‘come dire che un’officina di auto dovrebbe solo fare dei 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 test, ma a meno che non sia un refactoring massiccio, dovrò solo cambiarne alcuni e il fatto che gli altri test (invariati) interni funzionino ancora è un grande indicatore che il refactoring ha avuto successo.

Puoi provare a coprire tutti i casi interni utilizzando solo l’interfaccia pubblica e teoricamente è ansible testare tutti i metodi interni (o almeno quelli che contano) interamente utilizzando l’interfaccia pubblica, ma potresti dover finire in piedi per la 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 possono essere difficili o impossibili da discernere. Avendo indicato che i test individuali che garantiscono il corretto funzionamento della macchina interna valgono i piccoli cambiamenti dei test che avvengono con il refactoring, almeno questa è stata la mia esperienza. Se devi apportare enormi modifiche ai test per ogni refactoring, allora forse questo non ha senso, ma in tal caso, forse dovresti ripensare interamente il tuo design. Un buon design dovrebbe essere abbastanza flessibile da consentire la maggior parte dei cambiamenti senza ridisegni di grandi dimensioni.

Nei rari casi in cui ho voluto testare funzioni private, di solito li ho modificati per essere protetti, e ho scritto una sottoclass con una funzione di wrapper pubblico.

La class:

 ... protected void APrivateFunction() { ... } ... 

Sottoclass per il test:

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

Penso che una domanda più fondamentale dovrebbe essere posta perché provi a 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 class, mentre quel metodo è privato per un motivo in quanto è un dettaglio di implementazione. Uno dovrebbe essere interessato solo al comportamento dell’interfaccia pubblica non a come è implementato sotto le copertine.

Se voglio testare il comportamento del metodo privato, usando refactoring comuni, posso estrarne il codice in un’altra class (magari con visibilità a livello di pacchetto, quindi assicurati che non faccia parte di un’API pubblica). Posso quindi testare il suo comportamento in isolamento.

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

Posso quindi prendere in giro il suo comportamento quando provo a testare la class originale in modo che possa quindi concentrarmi sul test del comportamento dell’interfaccia pubblica di quella class piuttosto che dover testare un’esplosione combinatoria dell’interfaccia pubblica e il comportamento di tutti i suoi metodi privati .

Vedo questo analogo a guidare una macchina. Quando guido un’auto non guido con il cofano alzato, così posso vedere che il motore sta funzionando. Mi affido all’interfaccia fornita dall’auto, ovvero il contagiri e il tachimetro per sapere che il motore sta funzionando. 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 di esso separatamente. : 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 fosse refactored per consentire un test migliore. Michael Feathers ha scritto un grande libro su questo argomento. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052

Tipi privati, interni e membri privati ​​sono così a causa di qualche motivo, e spesso non vuoi metterli in discussione direttamente. E se lo fai, è probabile che ti romperai più tardi, perché non c’è alcuna garanzia che i ragazzi che hanno creato quegli assemblaggi manterranno le implementazioni private / interne in quanto tali.

Ma, a volte, quando faccio alcuni hack / esplorazione di assembly compilati o di terze parti, ho finito per voler inizializzare una class privata o una class con un costruttore privato o interno. Oppure, a volte, quando si tratta di librerie legacy pre-compilate che non riesco a modificare, finisco per scrivere alcuni test su un metodo privato.

Nasce così l’AccessPrivateWrapper – http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html – è una class di wrapper veloce che renderà più semplice il lavoro usando le caratteristiche e le riflessioni dinamiche 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(); 

Ho anche usato il metodo InternalsVisibleToAttribute. Vale anche la pena ricordare che, se ti senti a disagio nel rendere i tuoi metodi precedentemente privati ​​interni al fine di ottenere ciò, allora forse non dovrebbero essere comunque object di test unitari diretti.

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

Bene, puoi testare il metodo privato in due modi

  1. è ansible creare un’istanza della class PrivateObject la syntax è 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 }); 

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 le 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; } 

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. Metodi privati ​​statici di test unitario
  2. Test unitari Metodi privati ​​non statici

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

Su CodeProject, c’è un articolo che discute brevemente pro e contro di test di metodi privati. Quindi fornisce 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 sovraccaricati.

Puoi trovare l’articolo qui:

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

Dichiarali internal , quindi utilizza InternalsVisibleToAttribute per consentire al tuo assembly test dell’unità di visualizzarli.

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

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

Abbiamo aggiunto l’operatore “.-“. Si comporta come ‘.’ operatore, tranne che è ansible accedere a qualsiasi dichiarazione nascosta dai test senza modificare nulla nel progetto testato.

Dai un’occhiata al nostro sito Web: scaricalo gratuitamente .

In primo luogo, non dovresti testare i metodi privati ​​del tuo codice. Dovresti testare la ‘interfaccia pubblica’ o l’API, le cose pubbliche delle tue classi. Le API sono tutti i metodi pubblici che esponi ai chiamanti esterni.

Il motivo è che una volta che inizi a testare i metodi e gli interni privati ​​della tua class, accoppi l’implementazione della tua class (le cose private) ai tuoi test. Ciò significa che quando decidi di modificare i dettagli di implementazione dovrai anche cambiare i test.

Per questo motivo è necessario evitare l’uso di InternalsVisibleToAribribute.

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

MbUnit ha un bel wrapper per questo chiamato Reflector.

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

È inoltre ansible impostare e ottenere valori dalle proprietà

 dogReflector.GetProperty("Age"); 

Riguardo al “test privato” sono d’accordo che .. nel mondo perfetto. non ha senso fare test di unità private. Ma nel mondo reale potresti finire per voler scrivere test privati ​​invece del codice di refactoring.

Sono sorpreso che nessuno abbia ancora detto questo, ma una soluzione che ho impiegato è di fare un metodo statico all’interno della class per testare se stesso. Questo ti dà accesso a tutto ciò che è pubblico e privato con cui testare.

Inoltre, in un linguaggio di scripting (con le abilità di OO, come Python, Ruby e PHP), puoi eseguire il test del file stesso durante l’esecuzione. Bel modo veloce per assicurarti che le tue modifiche non abbiano infranto nulla. Questo ovviamente rende una soluzione scalabile per testare tutte le tue classi: basta eseguirle tutte. (puoi farlo anche in altre lingue con un main vuoto che esegue sempre i suoi test).

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

Nella tua class di test case includi solo questi metodi e poi li impieghi 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));

È sufficiente emettere i parametri nell’ordine in cui appaiono nella funzione privata originale

Per chiunque voglia eseguire metodi privati ​​senza tutti i problemi e il disordine. Funziona con qualsiasi framework di test delle unità usando nient’altro che il 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; } } 

Then in your actual tests, you can do something like this:

 Assert.AreEqual( ReflectionTools.InvokePrivate( typeof(StaticClassOfMethod), "PrivateMethod"), "Expected Result"); Assert.AreEqual( ReflectionTools.InvokePrivate( new ClassOfMethod(), "PrivateMethod"), "Expected Result"); 
 CC -Dprivate=public 

Here is good article about unit testing of private methods. But I’m not sure what’s better, to make you application designed specially for testing(it’s like creating tests for testing only) or use reflexion for testing. Pretty sure most of us will choose second way.

I use PrivateObject class. But as mentioned previously better to avoid testing private methods.

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

You could generate the test method for the private method from Visual studio 2008. When you create a unit test for a private method, a Test References folder is added to your test project and an accessor is added to that folder. The accessor is also referred to in the logic of the unit test method. This accessor allows your unit test to call private methods in the code that you are testing. For details have a look at

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

Also note that the InternalsVisibleToAtrribute has a requirement that your assembly be strong named , which creates it’s own set of problems if you’re working in a solution that had not had that requirement before. I use the accessor to test private methods. See this question that for an example of this.

Here’s an example, first the method signature:

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

Here’s the test:

 ///  ///A test for SplitInternal /// [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); } 

A way to do this is to have your method protected and write a test fixture which inherits your class to be tested. This way, you are nor turning your method public , but you enable the testing.

1) If you have a legacy code then the only way to test private methods is by reflection.

2) If it is new code then you have the following options:

  • Use reflection (to complicated)
  • Write unit test in the same class (makes the production code ugly by having test code also in it)
  • Refactor and make the method public in some kind of util class
  • Use @VisibleForTesting annotation and remove private

I prefer the annotation method, simplest and least complicated. The only issue is that we have increased the visibility which I think is not a big concern. We should always be coding to interface, so if we have an interface MyService and an implementation MyServiceImpl then we can have the corresponding test classs that is MyServiceTest (test interface methods) and MyServiceImplTest (test private methods). All clients should anyway be using the interface so in a way even though the visibility of the private method has been increased it should not really matter.

You could also declare it as public or internal (with InternalsVisibleToAttribute) while building in debug-Mode:

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

It bloats the code, but it will be private in a release build.