Kako testirate privatne metode?

Stvaram knjižnicu klasa koja će imati neke javne i privatne metode. Želim biti u mogućnosti testirati privatne metode (uglavnom u razvoju, ali također mogu biti korisne za buduće prerađivanje).

Koji je pravi način za to?

452
30 окт. postavio Eric Labashosky 30. listopada 2008-10-30 18:49 '08 u 18:49 2008-10-30 18:49
@ 33 odgovora
  • 1
  • 2

Ako koristite.net, morate koristiti InternalsVisibleToAttribute .

113
30 окт. odgovor je dat TcKs 30 okt. 2008-10-30 18:52 '08 u 18:52 2008-10-30 18:52

Ako želite privatnu metodu testiranja jedinice, nešto može biti pogrešno. Testovi jedinica (općenito govoreći) osmišljeni su za testiranje sučelja klase, što znači njezine javne (i zaštićene) metode. Možete, naravno, "hakirati" rješenje za ovo (čak i ako samo objavite metode), ali možda mislite:

border=0
  • Ako je metoda koju želite testirati uistinu vrijedna testiranja, vrijedno je da je premjestite u svoj razred.
  • Dodajte još testova javnim metodama koje pozivaju privatnu metodu provjerom funkcija privatne metode. (Kao što su primijetili komentatori, to biste trebali učiniti samo ako su ove funkcije privatnih metoda stvarno dio javnog sučelja. Ako zapravo izvode funkcije skrivene od korisnika (na primjer, test jedinice), to je vjerojatno loše).
335
30 окт. odgovor Jeroen Heijmans 30. listopada 2008-10-30 18:54 '08 u 18:54 2008-10-30 18:54

Možda je neprimjereno testirati privatne metode. Međutim, također ponekad volim zvati metode privatnog testiranja. Većinu vremena kako bi se spriječilo dupliciranje koda za generiranje testnih podataka ...

Microsoft nudi dva mehanizma za to:

Pristupnici

  • Idite na izvorni kod definicije klase
  • Desnom tipkom miša kliknite naziv klase.
  • Odaberite "Izradi privatnu dodatnu opremu"
  • Odaberite projekt u kojem će se pristupnik kreirati => Kao rezultat toga, dobit ćete novu klasu pod nazivom foo_accessor. Ova će se klasa dinamički generirati tijekom kompiliranja i učiniti dostupnim sve dostupne članove.

Meñutim, mehanizam je ponekad malo tvrdoglav kada se radi o mijenjanju sučelja izvorne klase. Većinu vremena izbjegavam ga koristiti.

PrivateObject class Drugi način je da koristite 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[] {} ); 
112
29 окт. odgovor dan Sedam 29. listopada. 2010-10-29 16:50 '10 u 16:50 2010-10-29 16:50

Ne slažem se s filozofijom da bi "trebali biti zainteresirani samo za ispitivanje vanjskog sučelja." To je pomalo kao da radionica za popravak automobila mora imati samo testove kako bi se vidjelo okreću li se kotači. Da, na kraju, ja sam zainteresiran za vanjsko ponašanje, ali volim svoje vlastite, privatne interne testove, koji će biti specifičniji i precizniji. Da, ako sam refactor, ja svibanj morati promijeniti neke od testova, ali ako to nije masivan refactoring, ja samo trebam promijeniti nekoliko, ali činjenica da druge (konstantne) interne testove i dalje rade je odličan pokazatelj da da je refactoring bio uspješan.

Možete pokušati pokriti sve interne slučajeve koristeći samo javno sučelje, a teoretski možete testirati svaku internu metodu (ili barem svaku stvar koja je bitna) u potpunosti, koristeći otvoreno sučelje, ali ćete možda morati sami stajati kako biste postigli ovo i veza između testnih slučajeva koji se provode kroz javno sučelje i unutrašnjost rješenja koje su dizajnirani za testiranje mogu biti teški ili nemogući. Naglašavanje pojedinačnih testova koji jamče ispravan rad unutarnje opreme vrijedi manje promjene u testovima vezanim za refactoring - barem da je moje iskustvo. Ako trebate napraviti velike promjene u svojim testovima za svaki refactoring, onda možda to nema smisla, ali u ovom slučaju, vi svibanj morati u potpunosti revidirati svoj dizajn. Dobar dizajn trebao bi biti dovoljno fleksibilan da prihvati većinu promjena bez masovnih redizajna.

73
19 окт. Odgovoriti Darrell Plank 19. lis. 2010-10-19 09:34 '10 u 9:34 2010-10-19 09:34

U rijetkim slučajevima, htio sam testirati privatne funkcije, obično sam ih izmijenio radi zaštite, i napisao sam potklasu s javno dostupnom funkcijom omotača.

klasa:

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

Podklasa za testiranje:

 ... [Test] public void TestAPrivateFunction() { APrivateFunction(); //or whatever testing code you want here } ... 
50
30 окт. Odgovorite Jasonu Jacksonu 30. listopada 2008-10-30 19:04 '08 u 19:04 2008-10-30 19:04

Mislim da je potrebno postaviti još jedno temeljno pitanje: zašto uopće pokušavate testirati određenu metodu? To je miris koda koji pokušavate testirati privatnu metodu putem javnog sučelja ove klase, dok je ova metoda privatna jer je to detalj implementacije. Trebali biste razmotriti samo ponašanje javnog sučelja, a ne način na koji se on provodi pod naslovom.

Ako želim testirati ponašanje privatne metode korištenjem općih prepravki, mogu izvući svoj kôd u drugu klasu (možda s vidljivošću na razini paketa kako bih bio siguran da nije dio javnog API-ja). Onda mogu provjeriti njegovo ponašanje u izolaciji.

Proizvod refactoringa znači da je privatna metoda sada zasebna klasa koja je koautor izvorne klase. Njegovo ponašanje bit će dobro shvaćeno uz pomoć vlastitih testova.

Tada mogu ismijavati svoje ponašanje kada pokušam testirati izvorni razred, a zatim se usredotočiti na testiranje ponašanja otvorenog sučelja ove klase, umjesto provjere kombinatorne eksplozije javnog sučelja i ponašanja svih njegovih privatnih metoda.

Vidim da je ovo slično vožnji automobila. Kad vozim auto, ne vozim s kapuljačom, tako da vidim da motor radi. Oslanjam se na sučelje koje automobil pruža, naime brojač okretaja i brzinomjer, da bi znao da motor radi. Oslanjam se na činjenicu da se automobil doista pomiče kad pritisnem papučicu gasa. Ako želim testirati motor, mogu provoditi provjere odvojeno

Naravno, testiranje privatnih metoda izravno može biti posljednje ako imate zastarjelu aplikaciju, ali ja bih radije da stari kôd bude reorganiziran kako bi osigurao bolje testiranje. Michael Perce napisao je sjajnu knjigu o toj temi. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052

22
24 окт. odgovor je dao Big Kahuna 24. listopada. 2012-10-24 19:30 '12 u 19:30 2012-10-24 19:30

Privatni tipovi, interni i privatni članovi iz nekog razloga, a često ih ne želite izravno kontaktirati. A ako to učinite, najvjerojatnije ćete krenuti dalje, jer nema jamstva da će dečki koji su stvorili ove sklopove spasiti privatne / interne implementacije kao takve.

Ali ponekad, kada se neki hakeri / studije sastavljaju ili grade treće strane, ja osobno na kraju želim inicijalizirati privatnu klasu ili klasu pomoću privatnog ili unutarnjeg konstruktora. Ili, ponekad, kada radite s pre-kompiliranim zastarjelim knjižnicama koje ne mogu promijeniti, završavam pisanjem nekih testova protiv određene metode.

Tako se pojavio AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - ovo je brza klasa omotača koja pojednostavljuje rad s dinamičkim funkcijama i refleksijama C # 4.0.

Možete stvoriti interne / privatne tipove, na primjer

  //Note that the wrapper is dynamic dynamic wrapper = AccessPrivateWrapper.FromType (typeof(SomeKnownClass).Assembly,"ClassWithPrivateConstructor"); //Access the private members wrapper.PrivateMethodInPrivateClass(); 
17
25 мая '10 в 10:44 2010-05-25 10:44 odgovor je dat amazedsaint 25. svibanj '10 u 10:44 2010-05-25 10:44

Pa, možete testirati privatnu metodu na dva načina.

  • možete stvoriti instancu klase PrivateObject , sintaksa je kako slijedi

     PrivateObject obj= new PrivateObject(PrivateClass); //now with this obj you can call the private method of PrivateCalss. obj.PrivateMethod("Parameters"); 
  • Možete koristiti refleksiju.

     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 }); 
10
27 марта '12 в 10:51 2012-03-27 10:51 odgovor je dan Nepoznat Ožujak 27 '12 u 10:51 2012-03-27 10:51

Također sam koristio metodu InternalsVisibleToAttribute. Također je vrijedno napomenuti da ako vam je neprikladno da svoje ranije privatne metode učinite unutarnjim da bi to postigli, možda u svakom slučaju ne bi trebali biti podvrgnuti izravnim jediničnim testovima.

Na kraju, testirate ponašanje svoje klase, a ne specifičnu implementaciju - možete je promijeniti bez mijenjanja prvoga, a testovi još moraju proći.

10
30 окт. odgovor od philsquared Oct 30 2008-10-30 18:55 '08 u 18:55 2008-10-30 18:55

Postoje dvije vrste privatnih metoda. Statičke privatne metode i ne-statičke privatne metode (metode primjera). Sljedeća 2 članka objašnjavaju kako jedinica testira privatne metode s primjerima.

8
22 июля '11 в 15:32 2011-07-22 15:32 odgovor je dao Venkat 22. srpnja '11 u 15:32 2011-07-22 15:32

MS Test ima lijepu funkcionalnost koja vam omogućuje stvaranje privatnih članova i metoda u projektu stvaranjem datoteke 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(); } } 

S klasama koje su proizvedene iz baze podataka

kao što su

 [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
30 окт. Odgovor je dao Marcus King 30. listopada. 2008-10-30 19:10 '08 u 19:10 2008-10-30 19:10

U CodeProjectu postoji članak koji ukratko razmatra prednosti i mane testiranja privatnih metoda. Zatim daje kod za razmišljanje za pristup privatnim metodama (slično Marcusovom brandu gore). Jedini problem s kojim sam se susreo u uzorku jest da kod ne uzima u obzir preopterećene metode.

Članak možete pronaći ovdje:

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

5
04 нояб. Odgovor je dan Pedro 04 Nov. 2008-11-04 21:35 '08 u 9:35 2008-11-04 21:35

Trudim se da ne koristim direktive o kompajlerima, jer oni brzo ometaju rad. Jedan od načina da ga omekšate, ako ih stvarno trebate, jest da ih stavite u djelomičnu klasu, a vaš skup ignorira ovu .cs datoteku prilikom kreiranja produkcijske verzije.

5
30 окт. odgovor je dao swilliams Lis 30 2008-10-30 18:59 '08 u 18:59 2008-10-30 18:59

Izjavite ih internal , a zatim upotrijebite InternalsVisibleToAttribute tako da ih jedinica za testiranje jedinica može vidjeti.

4
30 окт. Odgovor dao James Curran 30. listopada 2008-10-30 18:51 '08 u 18:51 2008-10-30 18:51

Prije svega, ne biste trebali provjeravati osobne metode koda. Trebali biste testirati "otvoreno sučelje" ili API, javne stvari vaših klasa. API-ji su sve javne metode koje pružate vanjskim pretplatnicima.

Razlog je u tome što nakon što počnete testirati privatne metode i unutrašnjost svoje klase, povezujete implementaciju svoje klase (određene stvari) sa svojim testovima. To znači da kada odlučite promijeniti podatke o implementaciji, također ćete morati promijeniti svoje testove.

Iz tog razloga izbjegavajte korištenje InternalsVisibleToAtrribute.

Sjajna priča Jan Coopera koja pokriva ovo pitanje: Ian Cooper: TDD, gdje je sve pošlo po zlu.

3
03 июля '14 в 3:49 2014-07-03 03:49 odgovor je dan cda01 3. srpnja '14. u 3:49 2014-07-03 03:49

Ponekad je korisno provjeriti privatne najave. U stvari, prevodilac ima samo jednu javnu metodu: Compile (string outputFileName, params string [] sourceSFileNames). Siguran sam da razumijete da bi bilo teško testirati takvu metodu bez testiranja svake "skrivene" deklaracije!

Zato smo stvorili Visual T #: kako bismo napravili lakše testove. Ovo je besplatan programski jezik .NET (kompatibilan s C # v2.0).

Dodali smo operatora ..-. Samo se ponaša kao '.' operateru, osim što možete pristupiti bilo kojem skrivenom oglasu iz svojih testova bez promjene u testnom projektu.

Pogledajte našu web stranicu: preuzmite besplatno .

3
01 мая '10 в 20:24 2010-05-01 20:24 Odgovor je dao Ludovic Dubois dana 1. svibnja 2010. u 20:24 2010-05-01 20:24

MbUnit je dobio lijep paket za ovo, nazvan Reflektor.

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

Također možete postaviti i dobiti vrijednosti iz svojstava

 dogReflector.GetProperty("Age"); 

Što se tiče "kviz testa", slažem se da ... u idealnom svijetu. nema smisla raditi privatne testove. No, u stvarnom svijetu, možda želite napisati privatne testove umjesto refactoring koda.

2
24 авг. odgovor dao Carl Bergquist 24. kolovoza. 2009-08-24 10:12 '09 u 10:12 2009-08-24 10:12

Za one koji žele voditi privatne metode bez ikakvih fesova i nereda. To radi s bilo kojim modulom za testiranje modula, koristeći samo dobre stare misli.

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

Tada u stvarnim testovima možete učiniti nešto poput ovoga:

 Assert.AreEqual( ReflectionTools.InvokePrivate( typeof(StaticClassOfMethod), "PrivateMethod"), "Expected Result"); Assert.AreEqual( ReflectionTools.InvokePrivate( new ClassOfMethod(), "PrivateMethod"), "Expected Result"); 
2
16 дек. Odgovor daje Erick Stone 16. prosinca. 2015-12-16 21:32 '15 u 21:32 2015-12-16 21:32

Iznenađen sam da nitko nije to rekao, ali rješenje koje sam upotrijebio je da napravim statičku metodu unutar razreda kako bih se testirao. To vam daje pristup svim javnim i privatnim, s kojima možete provjeriti.

Osim toga, u skriptnom jeziku (s OO mogućnostima kao što su Python, Ruby i PHP), možete pokrenuti samu probnu datoteku. Dobar brzi način kako biste bili sigurni da vaše promjene nisu ništa slomile. To očigledno čini skalabilno rješenje za testiranje svih vaših razreda: samo ih pokrenite. (To možete učiniti i na drugim jezicima s void main, koji također uvijek provodi testove.)

2
06 июня '11 в 12:24 2011-06-06 12:24 odgovor je dat rube 6. lipnja '11 u 12:24 2011-06-06 12:24

Želim ovdje stvoriti jednostavan primjer koda koji možete koristiti u bilo kojoj klasi u kojoj želite testirati privatnu metodu.

U vašem test slučaju, klasa jednostavno uključuje ove metode i onda ih primjenjuje kao što je navedeno.

   private $class_name_of_class_you_want_to_test_private_methods_in;  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; }  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', niz (param1, param2, param3));

Jednostavno unesite parametre redoslijedom kojim se pojavljuju u izvornoj privatnoj funkciji.

2
16 июля '15 в 22:34 2015-07-16 22:34 Odgovor daje Damon Hogan 16. srpnja u 22:34 sati 2015-07-16 22:34

Koristim PrivateObject razred. No, kao što je ranije spomenuto, bolje je izbjegavati testiranje privatnih metoda.

 Class target = new Class(); PrivateObject obj = new PrivateObject(target); var retVal = obj.Invoke("PrivateMethod"); Assert.AreEqual(retVal); 
1
21 апр. odgovor se daje vsapiha 21. travnja 2017-04-21 14:57 '17 u 2:57 pm 2017-04-21 14:57
 CC -Dprivate=public 
1
31 окт. Odgovor dao je Mark Harrison 31. listopada 2008-10-31 10:44 '08 u 10:44 2008-10-31 10:44

Ovdje je dobar članak o jedinstvenom testiranju privatnih metoda. Ali nisam siguran je li bolje napraviti vaš program posebno dizajniran za testiranje (to je kao stvaranje testova za testiranje) ili pomoću refleksije za testiranje. Jasno je da većina nas odabire drugi put.

1
11 апр. Odgovor dao Johnny_D 11. travnja 2012-04-11 18:14 '12 u 18:14 2012-04-11 18:14
1
12 февр. odgovor je dao Ruben Bartelink 12. veljače. 2010-02-12 12:23 '10 u 12:23 2010-02-12 12:23

Po mom mišljenju, jedinstveni test trebao bi objaviti vaš javni API.

Stvaranje javne metode za testiranje jedinice razbija enkapsulaciju, otkrivajući detalje implementacije.

Dobar javni API rješava neposredni cilj koda klijenta i potpuno rješava taj cilj.

0
26 нояб. odgovor od jpchauny 26. studenog 2016-11-26 17:33 '16 u 17:33 2016-11-26 17:33

Da biste to učinili, morate imati vlastitu protected metodu i napisati testni uređaj koji nasljeđuje testiranu klasu. Dakle, ne stavljate vašu metodu u public , ali uključite testiranje.

0
22 сент. Odgovor je dat oktopack 22. rujna 2014-09-22 21:34 '14 u 21:34 2014-09-22 21:34

Možete generirati testnu metodu za privatnu metodu iz Visual studia 2008. Kada stvorite test jedinice za privatnu metodu, testna se mapa dodaje test projektu i dodaje se dodatak u tu mapu. Pristupnik se također spominje u logici metode testiranja jedinica. Ovaj dodatak omogućuje jediničnom testu pozivanje privatnih metoda u kodu koji se testira. Za više informacija pogledajte.

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

0
ответ дан Sarath 28 мая '10 в 23:39 2010-05-28 23:39