abordarea 1: nu testați metodele Private

testarea metodelor Private cu JUnit și SuiteRunner
de Bill Venners
Mai 24, 2004
rezumat

acest articol compară patru abordări diferite pentru testarea metodelor private în clasele Java.

prima mea utilizare a JUnit a fost de a construi un kit de testare conformitate pentru API ServiceUI . Scopul unui kit de testare a conformității este de a vă asigura că implementările alternative ale aceluiași API sunt compatibile cu specificația API. Deoarece o specificație API definește doar interfața publică a API-ului, nu implementarea API-ului, un test de conformitate exercită doar interfața publică. Cu alte cuvinte, un test de conformitate este un test „cutie neagră”. Tratează API-ul testat ca o cutie neagră, a cărei interfață externă poate fi văzută, dar a cărei implementare internă nu poate. Prin urmare, un test de Conformitate al unui API Java trebuie să acceseze doar membrii publici ai pachetelor și claselor testate. Nu este nevoie să accesați membri la nivel de pachet, protejați sau privați.

când am aplicat mai târziu JUnit la sarcina de a scrie teste unitare reale, spre deosebire de testele de conformitate, m—am trezit dorind să scriu teste cu cutie albă-teste care folosesc cunoștințe despre implementarea internă a pachetelor și claselor testate. În timp ce am vrut doar să testez metode publice în testele mele de conformitate, am vrut să scriu teste unitare pentru accesul la pachete și, ocazional, metode private, precum și metode publice.

Daniel Steinberg mi-a arătat tehnica comună JUnit de a folosi arbori de cod sursă paralele, ceea ce mi-a permis să plasez clase de testare în același pachet ca și clasele testate, dar să le păstrez într-un director diferit. Aceasta a oferit o separare curată a codului de testare și producție. Plasând ambii arbori sursă în CLASSPATH, clasele mele de testare ar putea accesa metodele și clasele la nivel de pachet din pachetul testat. Acest lucru m-a lăsat totuși cu problema testării metodelor private.

când l-am întrebat pe Daniel despre testarea metodelor private, El mi-a sugerat cu blândețe să testez metodele private indirect prin testarea accesului la pachete și a metodelor publice care le numesc pe cele private. Acest răspuns nu m-a satisfăcut destul, pentru că uneori chiar am simțit nevoia de a testa direct o metodă privată. Soluția mea inițială a fost să fac doar astfel de metode private de acces la pachet, ceea ce mi-a permis să le testez direct cu JUnit din clasele de testare din același pachet din arborele sursă paralel. Acest lucru a lucrat bine, dar ma făcut să mă simt un pic murdar într-un fel. Deși, în general, am descoperit că gândirea la modul de proiectare a interfețelor, astfel încât acestea să poată fi testate cu ușurință în unitate, m-a ajutat să proiectez interfețe mai bune, în acest caz am simțit că fac designul puțin mai rău pentru a-l face testabil.

când am ajuns mai târziu să particip la crearea a ceea ce Frank Sommers, Matt Gerrans și, în cele din urmă , am lansat ca Artima SuiteRunner, am promis că voi face testarea metodelor private mai ușoară în SuiteRunner decât în JUnit. Dar după ce am investigat diferitele abordări ale testării metodelor private, am decis să nu fac nimic special în SuiteRunner pentru a sprijini testarea metodelor private. Deci, indiferent dacă utilizați JUnit sau SuiteRunner, aveți aceleași patru abordări de bază pentru testarea metodelor private:

  • nu testați metode private.
  • dă acces la pachetul de metode.
  • utilizați o clasă de testare imbricată.
  • utilizați reflexia.

în acest articol, voi discuta aceste patru abordări pentru testarea metodelor private în Java. Mă voi uita la avantajele și dezavantajele fiecăruia și voi încerca să fac lumină atunci când are sens să folosesc fiecare abordare.

după cum am menționat în introducere, am auzit pentru prima dată sfatul de a-mi suprima îndemnurile ocazionale de a testa metode private de la Daniel Steinberg. Dar Daniel nu este doar sursa acestui sfat pe care l-am întâlnit. Pare a fi o atitudine comună în comunitatea Java. De exemplu, JUnit FAQ afirmă:

testarea metodelor private poate fi un indiciu că aceste metode ar trebui mutate într-o altă clasă pentru a promova reutilizarea.

Charles Miller a exprimat un punct de vedere similar în blogul său:

dacă aveți o suită completă de teste pentru interfața expusă (non-privată) a unei clase, aceste teste ar trebui, prin natura lor, să verifice dacă orice metodă privată din clasă funcționează și ea. Dacă nu este cazul sau dacă aveți o metodă privată atât de complexă încât trebuie testată în afara contextului apelanților săi publici, aș considera că este un miros de cod.

și Dave Thomas și Andy Hunt , în cartea lor Pragmatic Unit Testing, scrie:

în general, nu doriți să rupeți nicio încapsulare de dragul testării (sau așa cum spunea mama, „nu vă expuneți părțile intime!”). De cele mai multe ori, ar trebui să puteți testa o clasă prin exercitarea metodelor sale publice. Dacă există o funcționalitate semnificativă care este ascunsă în spatele accesului privat sau protejat, acesta ar putea fi un semn de avertizare că există o altă clasă acolo care se luptă să iasă.

cred toate aceste sfaturi. De cele mai multe ori, metodele private pot fi testate cel mai eficient prin abordarea 1, indirect prin testarea metodelor la nivel de pachet, protejate și publice care le numesc. Dar, în mod inevitabil, unii oameni, în anumite situații, vor simți că testarea directă a unei metode private este ceea ce trebuie făcut.

în cazul meu, am tendința de a crea mai multe metode de utilitate privată. Aceste metode de utilitate nu fac adesea nimic cu datele instanței, ci doar operează pe parametrii trecuți și returnează un rezultat. Creez astfel de metode pentru a face metoda de apelare mai ușor de înțeles. Este o modalitate de a gestiona complexitatea implementării clasei. Acum, dacă extrag metoda privată dintr-o metodă care funcționează deja și are o acoperire bună a testului unitar, atunci acele teste unitare existente vor fi suficiente. Nu trebuie să scriu mai multe teste unitare doar pentru metoda privată. Dar dacă vreau să scriu metoda privată înainte de metoda de apelare și vreau să scriu testele unitare înainte de a scrie metoda privată, mă întorc la dorința de a testa direct metoda privată. În cazul metodelor de utilitate privată, nu simt nevoia mea de a testa direct metodele este, așa cum a spus JUnit FAQ, „o indicație că aceste metode ar trebui mutate într-o altă clasă pentru a promova reutilizarea.”Aceste metode sunt într-adevăr necesare doar în clasa în care locuiesc și, de fapt, sunt adesea numite doar printr-o altă metodă.

un alt motiv pentru care simt uneori nevoia de a testa direct metodele private este că tind să mă gândesc la testarea unității ca ajutându-mă să obțin un sistem robust construind acel sistem din părți robuste. Fiecare parte este o ” unitate „pentru care pot scrie” teste unitare.”Testele unitare mă ajută să mă asigur că fiecare unitate funcționează corect, ceea ce la rândul meu mă ajută să construiesc un sistem care funcționează corect în ansamblu. Unitatea primară cred că în ceea ce privește programarea în Java este clasa. Construiesc sisteme din clase, iar testele unitare îmi dau încredere că clasele mele sunt robuste. Dar, într-o anumită măsură, mă simt la fel despre metodele private din care compun pachetul-acces, protejat și metode publice. Aceste metode private sunt unități care pot fi testate individual. Astfel de teste unitare îmi dau încredere că metodele private funcționează corect, ceea ce mă ajută să construiesc metode de acces la pachete, protejate și publice care sunt robuste.

după cum am menționat în introducere, acordarea accesului la pachetul de metode a fost prima mea abordare pentru testarea metodelor private cu JUnit. Această abordare funcționează de fapt foarte bine, dar vine cu un cost ușor. Când văd un specificator de acces privat pe o metodă, îmi spune ceva ce îmi place să știu—că aceasta face parte din implementarea clasei. Știu că pot ignora metoda dacă încerc doar să folosesc clasa dintr-o altă clasă din pachet. Aș putea să-mi dau seama despre o metodă de acces la pachet, uitându-mă mai atent la numele, documentația și Codul metodei, dar cuvântul privat comunică acest lucru mult mai eficient. Mai mult, principala problemă pe care o am cu această abordare este filosofică. Deși nu mă deranjează „ruperea încapsulării de dragul testării”, așa cum ar spune Dave și Andy, pur și simplu nu mă simt bine să rup încapsularea într-un mod care schimbă API-ul la nivel de pachet. Cu alte cuvinte, deși sunt destul de entuziasmat să testez metode non-publice de clase, adică să creez teste unitare „cu cutie albă”, aș prefera ca API-ul claselor testate, inclusiv API-ul la nivel de pachet, să nu fie modificat pentru a facilita aceste teste.

abordarea 3: utilizați o clasă de testare imbricată

o a treia abordare a testării metodelor private este de a cuibări o clasă de testare statică în clasa de producție testată. Având în vedere că o clasă imbricată are acces la membrii privați ai clasei sale de închidere, ar putea invoca direct metodele private. Clasa statică în sine ar putea fi acces la pachet, permițându-i să fie încărcat ca parte a testului cutiei albe.

dezavantajul acestei abordări este că, dacă nu doriți ca clasa de testare imbricată să fie accesibilă în fișierul JAR de implementare, trebuie să faceți un pic de muncă suplimentară pentru a o extrage. De asemenea, este posibil ca unora să nu le placă să aibă codul de testare amestecat în același fișier ca și Codul de producție, deși alții ar putea prefera această abordare.

abordarea 4: Utilizați reflecția

a patra abordare a testării metodelor private mi-a fost sugerată de Vladimir R. Bossicard, care a scris JUnit Addons . Într-o zi la prânz, Vladimir mi-a luminat că API-ul java.lang.reflect a inclus metode care au permis codului clientului să eludeze mecanismul de protecție a accesului mașinii virtuale Java. De asemenea, mi-a spus că proiectul său JUnit Addons a inclus o clasă, junitx.util.PrivateAccessor, pentru a ajuta la utilizarea API-ului de reflecție doar în acest scop: să scrie teste unitare care să manipuleze membrii privați ai claselor testate. JUnit FAQ indică o clasă similară, numită PrivilegedAccessor, scrisă de Charlie Hubbard și Prashant Dhotke.

un avantaj al utilizării abordării de reflecție pentru testarea metodelor private este că oferă o separare curată a codului de testare și a codului de producție. Testele nu trebuie imbricate în interiorul clasei supuse încercării, ca în abordarea 3. Mai degrabă, ele pot fi plasate alături de celelalte teste care exercită metodele la nivel de pachet și publice ale clasei. În plus, nu trebuie să modificați API-ul clasei testate. Spre deosebire de abordarea 2, metodele private pot rămâne private. Spre deosebire de abordarea 3, nu trebuie să adăugați nicio clasă suplimentară imbricată la nivel de acces la pachete. Principalul dezavantaj al acestei abordări este că codul de testare este mult mai detaliat, deoarece folosește API-ul de reflecție. În plus, IDE-urile de refactorizare, cum ar fi Eclipse și IntelliJ, de obicei, nu sunt la fel de pricepuți la schimbarea numelor metodelor în care sunt denumite Strings transmise metodelor API de reflecție. Deci, dacă schimbați numele metodei private cu IDE-ul dvs. de refactorizare, este posibil să trebuiască să faceți unele modificări manual în codul de testare.

un exemplu

pentru a da un exemplu de metodă privată care, în opinia mea, merită testarea directă a unității, am extras unele funcționalități din metodamaindin clasaorg.suiterunner.Runner.Runner.mainanalizează argumentele din linia de comandă și rulează o suită de teste, declanșând opțional interfața grafică. Metoda pe care am extras-o,parseArgsIntoLists, face parte din activitatea de parsare a argumentelor liniei de comandă către aplicația SuiteRunner. Acum, pentru a testa metoda publică care numește această metodă privată, ar trebui să testezmain. Principala, desigur, este întreaga aplicație, ceea ce face ca metoda să fie destul de dificil de testat. De fapt, nu am nici un test existent pentrumain.

în acest moment, s-ar putea să vă întrebați, dacă scriam teste mai întâi în stilul dezvoltării bazate pe teste , cum am ajuns vreodată să scriu cod de analiză care nu avea teste unitare? Principalul motiv este că infecția mea de testare a venit în etape. De fapt, am prins o gripă de testare a unității cu mult înainte de a fi auzit de JUnit sau de testul citit infectat . Înapoi când am fost construirea de aplicații Windows în C++, de exemplu, mi-ar scrie un pic de cod pentru a testa o metodă nou implementată, apoi executa acel cod și urmăriți-l executa prin pas cu pas prin metoda testată cu debugger. Acest tip de testare unitară m-a ajutat să obțin robustețe, dar testele în sine nu au verificat comportamentul adecvat. Am verificat singur comportamentul adecvat observând prin depanator. Testele nu au fost automatizate și, prin urmare, nu le-am salvat pentru a putea fi rulate din nou mai târziu. Când am citit testul infectat, am văzut imediat valoarea automatizării testelor și păstrarea lor ca un fel de test de regresie după refactorizare, dar pentru o lungă perioadă de timp nu a avut sens pentru mine să scriu mai întâi testele. Am vrut să scriu testele după ce am implementat funcționalitatea, pentru că atunci am rulat testele cu depanatorul. Un motiv secundar pentru care nu am scris mai întâi teste în timp ce dezvoltam o mare parte din SuiteRunner este că am vrut să scriu testele lui SuiteRunner cu SuiteRunner în sine, într-un efort de a-mi mânca propria mâncare pentru câini. Până când API-ul de bază al lui SuiteRunner s-a stabilit, nu aveam setul de instrumente de testare pe care voiam să-l folosesc pentru a scrie testele.

din acel moment, cu toate acestea, virusul de testare a luat o dețin mai puternică pe mine, și eu acum prefer să scrie teste unitare mai întâi cele mai multe ori. Prefer să scriu teste mai întâi nu atât pentru că găsesc că ajung cu modele mai curate, care este de obicei promovat ca principalul beneficiu al dezvoltării bazate pe teste. Mai degrabă, prefer să scriu teste mai întâi pentru că mi se pare că de multe ori dacă mă scufund în cod sub presiune, cu intenția de a scrie testul mai târziu, testul nu se scrie niciodată. SuiteRunner în sine are foarte puține teste unitare în acest moment tocmai din acest motiv. Iată metoda parseArgsIntoLists :

 private static void parseArgsIntoLists(String args, List runpathList, List reportersList, List suitesList) { if (args == null || runpathList == null || reportersList == null || suitesList == null) { throw new NullPointerException(); } for (int i = 0; i < args.length; i++) { if (args.startsWith("-p")) { runpathList.add(args); runpathList.add(args); ++i; } else if (args.startsWith("-g")) { reportersList.add(args); } else if (args.startsWith("-o")) { reportersList.add(args); } else if (args.startsWith("-e")) { reportersList.add(args); } else if (args.startsWith("-f")) { reportersList.add(args); reportersList.add(args); ++i; } else if (args.startsWith("-r")) { reportersList.add(args); reportersList.add(args); ++i; } else if (args.startsWith("-s")) { suitesList.add(args); do { ++i; suitesList.add(args); } while (i + 1 < args.length); } else { throw new IllegalArgumentException("Unrecognized argument: " + args); } } }

linia de comandă pentru SuiteRunner conține trei tipuri de informații utilizate de SuiteRunner pentru a rula teste: runpath, reporteri și suite. Metoda parseArgsIntoListstrece doar prin argumentele transmise ca o matrice de String s și plasează fiecare argument într-una din liste, runpathList, reportersListși suitesList.

înainte de a scrie un test pentru această metodă privată, aș întreba dacă nevoia mea de a scrie acest test unitar reprezintă un miros de cod, așa cum Charles Miller a pus-o în blogul său? Indică faptul că parseArgsIntoLists ar trebui mutat într-o altă clasă pentru a promova reutilizarea, așa cum sugerează JUnit FAQ? Dave și Andy ar spune că este un semn de avertizare că există o altă clasă acolo care se luptă să iasă? Ei bine, poate. Aș putea crea concievably o clasă ArgumentsParser care deține doar câteva metode statice care efectuează lucrarea de parsare. Atât clasa ArgumentsParser, cât și metodele pe care le conține ar putea fi accesul la pachete, ceea ce le-ar face ușor de testat. Dar mie nu mi se pare corect. Aceste metode sunt numite doar de Runner.main. Se simt în mod clar ca metode private pentru mine. Singurul motiv pentru care le-aș muta într-o clasă ArgumentsParser este să le pot testa. De fapt, aș folosi abordarea numărul 2: Faceți accesul la pachetul de metode private.

în schimb, pentru acest exemplu am decis să iau abordarea numărul 4 și să folosesc reflecția. M-am uitat atât la Vladimir Bossicard ‘s junitx.utils.PrivateAccessor, cât și la Charlie Hubbard și Prashant Dhotke’ s PrivilegedAccessor, dar am decis că niciunul dintre ei nu m-a ajutat așa cum mi-am dorit. Pentru un singur lucru, aceste clase ambele au capacitatea de a testa câmpuri pentru a vă asigura că sunt setate corect. Până acum nu am simțit niciodată nevoia de a accesa direct câmpurile private din testele unitare. Vreau doar să pot testa metode de utilitate privată. Cu toate acestea, principala problemă pe care am avut-o cu aceste două clase este modul în care s-au ocupat de excepțiile care pot fi aruncate atunci când încercați să invocați metoda privată prin reflecție. Fiecare clasă are una sau mai multe metode a căror sarcină este invoca o metodă cu reflecție. Cele două metode PrivilegedAccessorale lui invokeMethod transmit orice excepție apelantului său, inclusiv trei excepții verificate declarate în clauza aruncări: NoSuchMethodException, IllegalAccessException și InvocationTargetException. În schimb, cele două metode PrivateAccessorale lui invoke prind InvocationTargetException și extrag și aruncă excepția țintă, excepția reală aruncată de metoda invocată. Apoi prinde orice altă excepție și aruncă NoSuchMethodException. Nu mi-a plăcut că apelantul din PrivilegedAccessor.invokeMethod va trebui întotdeauna să se ocupe de cele trei excepții verificate, pentru că m-am gândit că modul general de a trata orice excepție ar fi să lase testul să eșueze. Am fost, de asemenea, îngrijorat de faptul că PrivateAccessor.invoke arunca informații potențial utile despre urmărirea stivei în Politica sa de gestionare a excepțiilor. Ceea ce mi-am dorit cu adevărat a fost o metodă care a încercat să invoce o metodă privată cu reflecție, care a înfășurat orice excepție aruncată în afară de InvocationTargetException într-un TestFailedExceptionnecontrolat. De cele mai multe ori, această excepție ar determina eșecul testului. În testele care se așteptau ca o excepție să fie aruncată, excepția conținută în InvocationTargetException ar putea fi extrasă și testată pentru corectitudine.

prin urmare, am scris invokeStaticMethod. Apelul setAccessible(true) este ceea ce permite invocarea metodei private din afara clasei. O implementare invokeStaticMethod corespunzătoare pentru utilizare cu JUnit ar arunca AssertionFailedErrormai degrabă decât TestFailedException. Iată codul:

 private static void invokeStaticMethod(Class targetClass, String methodName, Class argClasses, Object argObjects) throws InvocationTargetException { try { Method method = targetClass.getDeclaredMethod(methodName, argClasses); method.setAccessible(true); method.invoke(null, argObjects); } catch (NoSuchMethodException e) { // Should happen only rarely, because most times the // specified method should exist. If it does happen, just let // the test fail so the programmer can fix the problem. throw new TestFailedException(e); } catch (SecurityException e) { // Should happen only rarely, because the setAccessible(true) // should be allowed in when running unit tests. If it does // happen, just let the test fail so the programmer can fix // the problem. throw new TestFailedException(e); } catch (IllegalAccessException e) { // Should never happen, because setting accessible flag to // true. If setting accessible fails, should throw a security // exception at that point and never get to the invoke. But // just in case, wrap it in a TestFailedException and let a // human figure it out. throw new TestFailedException(e); } catch (IllegalArgumentException e) { // Should happen only rarely, because usually the right // number and types of arguments will be passed. If it does // happen, just let the test fail so the programmer can fix // the problem. throw new TestFailedException(e); } }

apoi, am creat o metodă de comoditate care invocă metoda privată particulară pe care am vrut să o testez:

 private static void invokeParseArgsIntoLists(String args, List runpathList, List reportersList, List suitesList) throws InvocationTargetException { // Purposely pass null values to the method, to make sure it throws // NullPointerException Class argClasses = {String.class, List.class, List.class, List.class }; Object argObjects = {args, runpathList, reportersList, suitesList }; invokeStaticMethod(Runner.class, "parseArgsIntoLists", argClasses, argObjects); }

în sfârșit, aș putea scrie teste împotriva metodei private fără prea multă dezordine în exces, ca aceasta:

 public void testParseArgsIntoLists() throws InvocationTargetException { String args = new String; List runpathList = new ArrayList(); List reportersList = new ArrayList(); List suitesList = new ArrayList(); try { invokeParseArgsIntoLists(null, runpathList, reportersList, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, null, reportersList, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, runpathList, null, suitesList); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } try { invokeParseArgsIntoLists(args, runpathList, reportersList, null); fail(); } catch (InvocationTargetException e) { // throw the InvocationTargetException unless the target // exception is NullPointerException, which is expected Throwable targetException = e.getTargetException(); if (!(targetException instanceof NullPointerException)) { throw e; } } args = new String; args = "-p"; args = "\"mydir\""; args = "-g"; args = "-f"; args = "test.out"; args = "-s"; args = "MySuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 3); verify(reportersList.get(0).equals(args)); verify(reportersList.get(1).equals(args)); verify(reportersList.get(2).equals(args)); verify(suitesList.size() == 2); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); args = new String; args = "-p"; args = "\"mydir\""; args = "-e"; args = "-o"; args = "-r"; args = "MyCustomReporter"; args = "-s"; args = "MySuite"; args = "MyOtherSuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 4); verify(reportersList.get(0).equals(args)); verify(reportersList.get(1).equals(args)); verify(reportersList.get(2).equals(args)); verify(reportersList.get(3).equals(args)); verify(suitesList.size() == 3); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); verify(suitesList.get(2).equals(args)); args = new String; args = "-p"; args = "\"serviceuitest-1.1beta4.jar myjini http://myhost:9998/myfile.jar\""; args = "-g"; args = "-s"; args = "MySuite"; args = "MySecondSuite"; args = "MyThirdSuite"; args = "MyFourthSuite"; args = "MyFifthSuite"; args = "MySixthSuite"; runpathList.clear(); reportersList.clear(); suitesList.clear(); invokeParseArgsIntoLists(args, runpathList, reportersList, suitesList); verify(runpathList.size() == 2); verify(runpathList.get(0).equals(args)); verify(runpathList.get(1).equals(args)); verify(reportersList.size() == 1); verify(reportersList.get(0).equals(args)); verify(suitesList.size() == 7); verify(suitesList.get(0).equals(args)); verify(suitesList.get(1).equals(args)); verify(suitesList.get(2).equals(args)); verify(suitesList.get(3).equals(args)); verify(suitesList.get(4).equals(args)); verify(suitesList.get(5).equals(args)); verify(suitesList.get(6).equals(args)); }

concluzie

abordarea 1, testarea indirectă a metodelor private prin testarea metodelor la nivel de pachet, protejate și publice care le numesc, va fi adesea cea mai bună abordare. În cazurile în care doriți cu adevărat să testați direct metodele private, utilizarea reflecției pentru a testa metodele private, deși destul de greoaie, oferă cea mai curată separare a codului de testare de codul de producție și cel mai mic impact asupra codului de producție. Cu toate acestea, dacă nu vă deranjează să faceți acele metode private particulare pe care doriți să le testați accesul la pachete, puteți utiliza abordarea 2. Sau dacă nu vă deranjează să plasați o clasă de testare imbricată în clasa dvs. de producție testată, abordarea 3 vă va permite cel puțin să păstrați metodele private private.

nu există un răspuns perfect. Dar dacă adoptați abordarea 4, veți ajunge în cele din urmă cu o mână de metode precum invokeStaticMethod pe care le puteți reutiliza. După ce scrieți o metodă de comoditate, cum ar fi invokeParseArgsIntoLists, pentru o metodă privată, puteți scrie teste împotriva metodei private fără prea multe dificultăți.

resurse

1. API-ul ServiceUI definește o modalitate standard de a atașa interfețe utilizator la serviciile Jini:
http://www.artima.com/jini/serviceui/index.html

2. Daniel Steinberg este în prezent redactor-șef al Java.NET:
http://www.java.net/

3. Artima SuiteRunner este un set gratuit de instrumente de testare open source și JUnit runner:
http://www.artima.com/suiterunner/index.html

4.JUnit Întrebări frecvente despre testarea metodelor private:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10

5. Testarea metodelor Private (nu o faceți), o postare pe blog de Charles Miller:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it

6. Andy Hunt și Dave Thomas sunt autorii Pragmatic Unit Testing, care este disponibil la magazinul Pragmatic.

7. JUnit Addons este o colecție de clase de ajutor pentru JUnit creat de Vladimar R. Bossicard:
http://sourceforge.net/projects/junit-addons

PrivateAccessor este clasa de la JUnit Addons care facilitează testarea membrilor privați:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html

9.Clasa PrivilegedAccessor, pe care o puteți utiliza pentru a accesa membrii privați:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java

10. Dezvoltarea bazată pe Test de exemplu, de Kent Beck, descrie prima tehnică de testare:
http://www.amazon.com/exec/obidos/ASIN/0321146530/

11. Testul infectat, de Kent Beck și Erich Gamma, a introdus JUnit în lume:
http://junit.sourceforge.net/doc/testinfected/testing.htm

testarea unitară a metodelor Private, o postare pe blog despre nUnit de Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx

subminarea protecției de acces Java pentru testarea unității, un O ‘ Reilly OnJava.com etichetă: Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html

clasa RunnerSuite, din care au fost preluate fragmentele de cod din acest articol, apare în întregime aici:
http://www.artima.com/suiterunner/privateExample.html

de ce am Refactorizat JUnit
http://www.artima.com/suiterunner/why.html

Artima SuiteRunner Tutorial, construirea conformității și teste unitare cu Artima SuiteRunner:
http://www.artima.com/suiterunner/tutorial.html

Noțiuni de bază cu Artima SuiteRunner, cum să rulați exemplul simplu inclus în distribuție:
http://www.artima.com/suiterunner/start.html

Runnning JUnit testează cu Artima SuiteRunner, cum să utilizați Artima SuiteRunner ca JUnit runner pentru a rula suitele de testare JUnit existente:
http://www.artima.com/suiterunner/junit.html

pagina principală Artima SuiteRunner:
http://www.artima.com/suiterunner/index.html

Artima SuiteRunner pagina de descărcare (trebuie să vă conectați pe Artima.com pentru a descărca versiunea):
http://www.artima.com/suiterunner/download.jsp

Forumul SuiteRunner:
http://www.artima.com/forums/forum.jsp?forum=61

răspunde!

ai o părere? Cititorii au postat deja 26 de comentarii despre acest articol. De ce să nu-l adaugi pe al tău?

despre autor

Bill Venners este președintele Artima Software, Inc. și redactor-șef al Artima.com. el este autorul cărții, în interiorul mașinii virtuale Java, Un sondaj orientat spre programator al arhitecturii și Internelor platformei Java. Coloanele sale populare din revista JavaWorld au acoperit java internals, design orientat pe obiecte și Jini. Bill a fost activ în comunitatea Jini încă de la începuturile sale. El a condus Proiectul ServiceUI al Comunității Jini care a produs API-ul ServiceUI. ServiceUI a devenit modul standard de facto de a asocia interfețele utilizatorului cu serviciile Jini și a fost primul standard comunitar Jini aprobat prin procesul de decizie Jini. Bill servește, de asemenea, ca membru ales al Comitetului inițial de Supraveghere Tehnică (TOC) al Comunității Jini și, în acest rol, a contribuit la definirea procesului de guvernare pentru comunitate. În prezent își dedică cea mai mare parte a energiei construirii Artima.com într-o resursă tot mai utilă pentru dezvoltatori.

You might also like

Lasă un răspuns

Adresa ta de email nu va fi publicată.