denne artikel sammenligner fire forskellige tilgange til test af private metoder i Java-klasser.
min allerførste brug af JUnit var at opbygge et overensstemmelsestestkit til ServiceUI API . Formålet med et overensstemmelsestestkit er at hjælpe med at sikre, at alternative implementeringer af den samme API er kompatible med API ‘ ens specifikation. Da en API-specifikation kun definerer API ‘ens offentlige grænseflade, ikke API’ ens implementering, udøver en overensstemmelsestest kun den offentlige grænseflade. Med andre ord er en overensstemmelsestest en” sort boks ” – test. Det behandler API ‘ en under test som en sort boks, hvis eksterne grænseflade kan ses, men hvis interne implementering ikke kan. En overensstemmelsestest af en Java API behøver derfor kun at få adgang til de offentlige medlemmer af de pakker og klasser, der testes. Der er ikke behov for at få adgang til pakkeniveau, beskyttet, eller private medlemmer.
da jeg senere anvendte JUnit til opgaven med at skrive faktiske enhedstest i modsætning til overensstemmelsestest, fandt jeg mig selv lyst til at skrive hvide bokstest—tests, der anvender viden om den interne implementering af de pakker og klasser, der testes. Mens jeg kun ønskede at teste offentlige metoder i mine overensstemmelsestests, ville jeg skrive enhedstest for pakkeadgang og lejlighedsvis private metoder såvel som offentlige metoder.
Daniel Steinberg viste mig den almindelige JUnit-teknik til at bruge parallelle kildekodetræer, som gjorde det muligt for mig at placere testklasser i samme pakke som de klasser, der blev testet, men opbevare dem i en anden mappe. Dette gav en ren adskillelse af test-og produktionskode. Ved at placere begge kildetræer i CLASSPATH kunne mine testklasser få adgang til pakkeniveaumetoder og klasser i pakken under test. Dette efterlod mig dog stadig problemet med at teste private metoder.
da jeg spurgte Daniel om at teste private metoder, foreslog han forsigtigt, at jeg testede de private metoder indirekte ved at teste pakken-adgang og offentlige metoder, der kalder de private. Dette svar tilfredsstillede mig ikke helt, for lejlighedsvis følte jeg virkelig trangen til direkte at teste en privat metode. Min oprindelige løsning var at bare gøre sådanne private metoder pakke adgang, som tillod mig at teste dem direkte med JUnit fra testklasserne i samme pakke i det parallelle kildetræ. Dette fungerede fint, men fik mig til at føle mig lidt beskidt på en eller anden måde. Selvom jeg generelt opdagede, at det at tænke på, hvordan man designer grænseflader, så de let kunne testes, hjalp mig med at designe bedre grænseflader, i dette tilfælde følte jeg, at jeg gjorde designet lidt værre for at gøre det testbart.
da jeg senere endte med at deltage i oprettelsen af, hvad Frank Sommers, Matt Gerrans , og til sidst frigav som Artima SuiteRunner, lovede jeg, at jeg ville gøre testningen af private metoder lettere i SuiteRunner end det er i JUnit. Men efter at have undersøgt de forskellige tilgange til test af private metoder, jeg besluttede ikke at gøre noget særligt i SuiteRunner for at støtte test af private metoder. Så uanset om du bruger JUnit eller SuiteRunner, du har de samme fire grundlæggende tilgange til at teste private metoder:
- test ikke private metoder.
- Giv metodepakken adgang.
- brug en indlejret testklasse.
- brug refleksion.
i denne artikel vil jeg diskutere disse fire tilgange til test af private metoder i Java. Jeg vil se på fordele og ulemper ved hver og forsøge at kaste lys over, når det giver mening at bruge hver tilgang.
som jeg nævnte i indledningen, hørte jeg først rådene om at undertrykke mine lejlighedsvise opfordringer til at teste private metoder fra Daniel Steinberg. Men Daniel er ikke kun kilden til dette råd, som jeg er stødt på. Det ser ud til at være en fælles holdning i Java-samfundet. For eksempel siger JUnit ofte stillede spørgsmål:
test af private metoder kan være en indikation af, at disse metoder skal flyttes til en anden klasse for at fremme genanvendelighed.
Charles Miller udtrykte et lignende synspunkt i sin blog:
hvis du har en grundig testpakke til en klasses eksponerede (ikke-private) grænseflade, skal disse tests efter deres natur kontrollere, at enhver privat metode inden for klassen også fungerer. Hvis dette ikke er tilfældet, eller hvis du har en privat metode, der er så kompleks, at den skal testes ud fra sammenhængen med dens offentlige opkaldere, ville jeg betragte det som en kodelugt.
og Dave Thomas og Andy Hunt skriver i deres bog Pragmatic Unit Testing:
generelt ønsker du ikke at bryde nogen indkapsling af hensyn til test (eller som mor plejede at sige, “Udsæt ikke dine menige!”). Det meste af tiden skal du være i stand til at teste en klasse ved at udøve dens offentlige metoder. Hvis der er betydelig funktionalitet, der er skjult bag privat eller beskyttet adgang, kan det være et advarselsskilt om, at der er en anden klasse derinde, der kæmper for at komme ud.
jeg tror på alt dette råd. Det meste af tiden, private metoder kan testes mest effektivt via tilgang 1, indirekte ved at teste pakkeniveauet, beskyttet, og offentlige metoder, der kalder dem. Men uundgåeligt vil nogle mennesker i nogle situationer føle, at direkte test af en privat metode er den rigtige ting at gøre.
i mit tilfælde har jeg en tendens til at oprette mange private brugsmetoder. Disse hjælpemetoder gør ofte intet med forekomstdata, de fungerer bare på de beståede parametre og returnerer et resultat. Jeg opretter sådanne metoder for at gøre opkaldsmetoden lettere at forstå. Det er en måde at styre kompleksiteten af implementeringen af klassen. Hvis jeg nu udtrækker den private metode ud af en metode, der allerede fungerer og har god enhedstestdækning, vil de eksisterende enhedstest sandsynligvis være tilstrækkelige. Jeg behøver ikke skrive flere unit tests bare for den private metode. Men hvis jeg vil skrive den private metode før dens opkaldsmetode, og jeg vil skrive enhedstestene, før jeg skriver den private metode, er jeg tilbage til at ville teste den private metode direkte. I tilfælde af private brugsmetoder, jeg føler ikke, at min trang til direkte at teste metoderne er, som JUnit ofte stillede spørgsmål udtrykte det, “en indikation af, at disse metoder skal flyttes til en anden klasse for at fremme genanvendelighed.”Disse metoder er virkelig kun nødvendige i den klasse, hvor de bor, og kaldes faktisk ofte kun af en anden metode.
en anden grund til, at jeg nogle gange føler trang til at teste private metoder direkte, er, at jeg har en tendens til at tænke på enhedstest som at hjælpe mig med at opnå et robust system ved at bygge dette system ud af robuste dele. Hver del er en” enhed”, som jeg kan skrive ” enhedstest.”Enhedstestene hjælper mig med at sikre, at hver enhed fungerer korrekt, hvilket igen hjælper mig med at opbygge et system, der fungerer korrekt som helhed. Den primære enhed, jeg tror med hensyn til programmering i Java, er klassen. Jeg bygger systemer ud af klasser, og enhedstest giver mig tillid til, at mine klasser er robuste. Men til en vis grad føler jeg også det samme med de private metoder, som jeg komponerer pakke-adgang, beskyttet, og offentlige metoder. Disse private metoder er enheder, der kan testes individuelt. Sådanne enhedstest giver mig tillid til, at de private metoder fungerer korrekt, hvilket hjælper mig med at opbygge pakkeadgang, beskyttet, og offentlige metoder, der er robuste.
som jeg nævnte i indledningen, at give metoder pakke adgang var min første tilgang til at teste private metoder med JUnit. Denne tilgang fungerer faktisk fint, men det kommer med en lille pris. Når jeg ser en privat adgangsspecifikator på en metode, fortæller det mig noget, jeg gerne vil vide—at dette er en del af implementeringen af klassen. Jeg ved, at jeg kan ignorere metoden, hvis jeg bare prøver at bruge klassen fra en anden klasse i pakken. Jeg kunne finde ud af dette om en pakke-adgangsmetode ved at se nærmere på navnet, dokumentationen og koden for metoden, men ordet privat kommunikerer dette langt mere effektivt. Desuden er det største problem, jeg har med denne tilgang, filosofisk. Selvom jeg ikke har noget imod at “bryde indkapsling for testens Skyld”, som Dave og Andy ville sige det, har jeg det bare ikke godt med at bryde indkapsling på en måde, der ændrer API på pakkeniveau. Med andre ord, selvom jeg er ret begejstret for at teste ikke-offentlige metoder til klasser, dvs.at oprette “hvidboks” enhedstest, vil jeg hellere API ‘ en for de klasser, der testes, inklusive API på pakkeniveau, ikke ændres for at lette disse tests.
tilgang 3: Brug en indlejret Testklasse
en tredje tilgang til test af private metoder er at indlejre en statisk testklasse inden for den produktionsklasse, der testes. I betragtning af at en indlejret klasse har adgang til de private medlemmer af sin omsluttende klasse, ville den være i stand til at påberåbe sig de private metoder direkte. Selve den statiske klasse kan være pakkeadgang, så den kan indlæses som en del af den hvide bokstest.
ulempen ved denne tilgang er, at hvis du ikke ønsker, at den indlejrede testklasse er tilgængelig i din deployment JAR-fil, skal du gøre lidt ekstra arbejde for at udtrække den. Også nogle mennesker kan ikke lide at have testkode blandet i samme fil som produktionskode, selvom andre måske foretrækker den tilgang.
tilgang 4: Brug refleksion
den fjerde tilgang til test af private metoder blev foreslået for mig af Vladimir R. Bossicard, der skrev JUnit Addons . En dag over frokosten oplyste Vladimir mig, at API ‘ en java.lang.reflect
indeholdt metoder, der gjorde det muligt for klientkode at omgå adgangsbeskyttelsesmekanismen for Java virtual machine. Han fortalte mig også, at hans JUnit Addons-projekt omfattede en klasse, junitx.util.PrivateAccessor
, for at hjælpe med at bruge reflection API til netop dette formål: at skrive enhedstest, der manipulerer private medlemmer af de klasser, der testes. JUnit ofte stillede spørgsmål peger på en lignende klasse, kaldet PrivilegedAccessor
, skrevet af Charlie Hubbard og Prashant Dhotke.
en fordel ved at bruge refleksionsmetoden til at teste private metoder er, at den giver en ren adskillelse af testkode og produktionskode. Testene behøver ikke at være indlejret i den klasse, der testes, som i tilgang 3. Snarere kan de placeres sammen med de andre tests, der udøver klassens pakkeniveau og offentlige metoder. Derudover behøver du ikke ændre API ‘ en for den klasse, der testes. I modsætning til tilgang 2 kan private metoder forblive private. I modsætning til tilgang 3 behøver du ikke tilføje nogen ekstra indlejret klasse på pakkeadgangsniveau. Den største ulempe ved denne tilgang er, at testkoden er langt mere detaljeret, fordi den bruger reflection API. Derudover er refactoring ide ‘ er som Eclipse og IntelliJ normalt ikke så dygtige til at ændre navnene på metoder, hvor de kaldes String
s, der overføres til metoderne i reflection API. Så hvis du ændrer navnet på den private metode med din refactoring IDE, skal du muligvis stadig foretage nogle ændringer manuelt i testkoden.
et eksempel
for at give et eksempel på en privat metode, der efter min mening fortjener direkte enhedstest, ekstraherede jeg nogle funktioner ud afmain
klassemetodenorg.suiterunner.Runner
.Runner.main
analyserer kommandolinjeargumenter og kører en række tests, der eventuelt fyrer GUI ‘ en op. Metoden Jeg ekstraherede,parseArgsIntoLists
, gør en del af arbejdet med at analysere kommandolinjens argumenter til SuiteRunner-applikationen. For at teste den offentlige metode, der kalder denne private metode, skal jeg testemain
. Main er selvfølgelig hele applikationen, hvilket gør metoden ret vanskelig at teste. Faktisk har jeg ingen eksisterende test formain
.
på dette tidspunkt undrer du dig måske, hvis jeg først skrev test i stil med testdrevet udvikling, hvordan endte jeg nogensinde med at skrive parsing kode, der ikke havde nogen enhedstest? Hovedårsagen er, at min testinfektion er kommet i etaper. Jeg faktisk fange en enhed test flu længe før jeg havde hørt om JUnit eller læse Test inficeret . Tilbage da jeg byggede vinduer applikationer i C++, for eksempel, ville jeg skrive en smule kode for at teste en nyligt implementeret metode, derefter udføre den kode og se den udføre ved at træde gennem metoden under test med debuggeren. Denne form for enhedstest hjalp mig med at opnå robusthed, men testene selv kontrollerede ikke for den korrekte adfærd. Jeg kontrollerede selv for den korrekte adfærd ved at observere via debuggeren. Testene blev ikke automatiseret, og derfor gemte jeg dem ikke, så de kunne køres igen senere. Da jeg læste Testinficeret, så jeg straks værdien af at automatisere testene og holde dem rundt som en slags regressionstest efter refactoring, men i lang tid var det ikke fornuftigt for mig at skrive testene først. Jeg ville skrive testene, efter at jeg implementerede funktionaliteten, for det var da jeg havde kørt testene med debuggeren. En sekundær grund til, at jeg ikke skrev tests først, mens jeg udviklede meget af SuiteRunner, er, at jeg ønskede at skrive SuiteRunner ‘ s tests med SuiteRunner selv i et forsøg på at spise min egen hundemad. Indtil SuiteRunner ‘ s basic API blev afgjort, havde jeg ikke det testværktøjssæt, jeg ønskede at bruge til at skrive testene.
siden da har testvirus imidlertid taget et stærkere greb om mig, og jeg foretrækker nu at skrive enhedstest først det meste af tiden. Jeg foretrækker at skrive test først ikke så meget, fordi jeg finder ud af, at jeg ender med renere design, som normalt fremmes som den største fordel ved testdrevet udvikling. Hellere, jeg foretrækker at skrive test først, fordi jeg finder det ofte, hvis jeg dykker ned i koden under pres, med den hensigt, at jeg skriver testen senere, testen bliver faktisk aldrig skrevet. SuiteRunner selv har meget få enhedstest på dette tidspunkt af netop den grund. Her er parseArgsIntoLists
metoden:
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); } } }
kommandolinjen til SuiteRunner indeholder tre typer oplysninger, der bruges af SuiteRunner til at køre tests: runpath, reportere og suiter. Metoden parseArgsIntoLists
gennemgår blot de argumenter, der er bestået som en række String
s, og placerer hvert argument i en af listerne, runpathList
, reportersList
og suitesList
.
før jeg skriver en test for denne private metode, vil jeg spørge, om min trang til at skrive denne enhedstest repræsenterer en kodelugt, som Charles Miller udtrykte det i sin blog? Indikerer det, at parseArgsIntoLists
skal flyttes til en anden klasse for at fremme genanvendelighed, som JUnit ofte stillede spørgsmål antyder? Ville Dave og Andy sige, at det er et advarselsskilt, at der er en anden klasse derinde, der kæmper for at komme ud? Nå, måske. Jeg kunne koncievably oprette en ArgumentsParser
klasse, der kun indeholder et par statiske metoder, der udfører parsing arbejde. Både ArgumentsParser
– klassen og de metoder, den indeholder, kunne være pakkeadgang, hvilket ville gøre dem lette at teste. Men det føles bare ikke rigtigt for mig. Disse metoder kaldes kun af Runner.main
. De føles helt klart som private metoder for mig. Den eneste grund til at jeg ville flytte dem til en ArgumentsParser
klasse er at kunne teste dem. Jeg ville faktisk bruge tilgang nummer 2: få adgang til den private metodepakke.
i stedet besluttede jeg for dette eksempel at tage tilgang nummer 4 og bruge refleksion. Jeg kiggede på både Vladimir Bossicards junitx.utils.PrivateAccessor
og Charlie Hubbard og Prashant Dhotkes PrivilegedAccessor
, men besluttede, at ingen af dem hjalp mig helt som jeg ønskede. For det første har disse klasser begge evnen til at teste felter for at sikre, at de er indstillet korrekt. Endnu har jeg aldrig følt nogen trang til direkte adgang til private felter fra enhedstest. Jeg vil bare være i stand til at teste private utility metoder. Det største problem, jeg havde med disse to klasser, imidlertid, er, hvordan de håndterede de undtagelser, der kan kastes, når de prøver at påberåbe sig den private metode via refleksion. Hver klasse har en eller flere metoder, hvis job det er påberåbe sig en metode med refleksion. PrivilegedAccessor
‘ s to invokeMethod
metoder overfører enhver undtagelse tilbage til sin opkalder, herunder tre kontrollerede undtagelser, der er erklæret i kasteklausulen: NoSuchMethodException
, IllegalAccessException
og InvocationTargetException
. Derimod PrivateAccessor
‘s to invoke
metoder fange InvocationTargetException
, og udtrække og smide målet undtagelse, den faktiske undtagelse kastet af den påberåbte metode. Den fanger derefter enhver anden undtagelse og kaster NoSuchMethodException
. Jeg kunne ikke lide, at opkalderen af PrivateAccessor.invoke kastede potentielt nyttige stack trace-oplysninger i sin undtagelseshåndteringspolitik. Hvad jeg virkelig ønskede var en metode, der forsøgte at påberåbe sig en privat metode med refleksion, som indpakket enhver kastet undtagelse udover InvocationTargetException
i en ukontrolleret TestFailedException
. Det meste af tiden vil denne undtagelse få testen til at mislykkes. I test, der forventede en undtagelse, der skulle kastes, kunne undtagelsen indeholdt i InvocationTargetException
udvindes og testes for korrekthed.
derfor skrev jeg invokeStaticMethod
. setAccessible(true)
– opkaldet er det, der gør det muligt at påberåbe sig den private metode uden for klassen. En tilsvarende invokeStaticMethod
implementering til brug med JUnit ville kaste AssertionFailedError
snarere end TestFailedException
. Her er koden:
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); } }
næste, jeg oprettede en bekvemmelighedsmetode, der påberåber sig den særlige private metode, jeg ville teste:
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); }
endelig kunne jeg skrive tests mod den private metode uden for meget overskydende rod, som dette:
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)); }
konklusion
tilgang 1, test af private metoder indirekte ved at teste pakkeniveau, beskyttede og offentlige metoder, der kalder dem, vil ofte være den bedste tilgang. I tilfælde, hvor du virkelig ønsker at teste private metoder direkte, ved hjælp af refleksion til at teste private metoder, selvom det er ret besværligt, giver den reneste adskillelse af testkode fra produktionskode og den mindste indvirkning på produktionskoden. Men hvis du ikke har noget imod at lave de særlige private metoder, du vil teste pakkeadgang, kan du bruge tilgang 2. Eller hvis du ikke har noget imod at placere en indlejret testklasse i din produktionsklasse under test, vil tilgang 3 i det mindste lade dig holde de private metoder private.
der er ikke et perfekt svar. Men hvis du vedtager tilgang 4, vil du i sidste ende ende med en håndfuld metoder som invokeStaticMethod
, som du kan genbruge. Når du først har skrevet en bekvemmelighedsmetode, som invokeParseArgsIntoLists
, til en privat metode, kan du skrive tests mod den private metode uden meget besvær.
ressourcer
1. ServiceUI API definerer en standard måde at vedhæfte brugergrænseflader til Jini-tjenester:
http://www.artima.com/jini/serviceui/index.html
2. Daniel Steinberg er i øjeblikket chefredaktør for Java.NET:
http://www.java.net/
3. Artima SuiteRunner er en gratis open source test toolkit og JUnit runner:
http://www.artima.com/suiterunner/index.html
4.Spørgsmål om test af private metoder:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10
5. Test af Private metoder (gør det ikke), en blog post af Charles Miller:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it
6. Andy Hunt og Dave Thomas er forfatterne af Pragmatic Unit Testing, som er tilgængelig i Pragmatic Store.
7. JUnit Addons er en samling af hjælperklasser for JUnit oprettet af Vladimar R. Bossicard:
http://sourceforge.net/projects/junit-addons
PrivateAccessor
er klassen fra JUnit Addons, der letter test af private medlemmer:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html
9.PrivilegedAccessor klasse, som du kan bruge til at få adgang til private medlemmer:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java
10. Testdrevet udvikling ved Eksempel, af Kent Beck, beskriver test-første teknik:
http://www.amazon.com/exec/obidos/ASIN/0321146530/
11. Test inficeret, af Kent Beck og Erich Gamma, introducerede JUnit til verden:
http://junit.sourceforge.net/doc/testinfected/testing.htm
Unit Testing Private Methods, en blog indlæg om nUnit af Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx
undergravning af Java ‘s adgangsbeskyttelse til enhedstest, en O’ Reilly OnJava.com artikel af Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html
klasse RunnerSuite
, hvorfra kodestykkerne i denne artikel blev taget, vises i sin helhed her:
http://www.artima.com/suiterunner/privateExample.html
hvorfor vi Refactored JUnit
http://www.artima.com/suiterunner/why.html
Artima SuiteRunner Tutorial, Bygningskonformance og enhedstest med Artima SuiteRunner:
http://www.artima.com/suiterunner/tutorial.html
Kom godt i gang med Artima SuiteRunner, Sådan kører du det enkle eksempel, Der er inkluderet i distributionen:
http://www.artima.com/suiterunner/start.html
Runnning JUnit tester med Artima SuiteRunner, hvordan du bruger Artima SuiteRunner som en JUnit runner til at køre dine eksisterende JUnit test suiter:
http://www.artima.com/suiterunner/junit.html
Artima SuiteRunner hjemmeside:
http://www.artima.com/suiterunner/index.html
Artima SuiteRunner Hent side (du skal logge på Artima.com Sådan hentes udgivelsen):
http://www.artima.com/suiterunner/download.jsp
SuiteRunner Forum:
http://www.artima.com/forums/forum.jsp?forum=61
Tal tilbage!
har du en mening? Læsere har allerede sendt 26 kommentarer til denne artikel. Hvorfor ikke tilføje din?
om forfatteren
Bill Venners er formand for Artima. og chefredaktør for Artima.com. han er forfatter til bogen, inde i Java Virtual Machine, en programmørorienteret undersøgelse af Java-platformens arkitektur og interne. Hans populære kolonner i Javaverdensmagasinet dækkede Java internals, objektorienteret design og Jini. Bill har været aktiv i Jini-samfundet siden starten. Han ledede Jini-samfundets ServiceUI-projekt, der producerede ServiceUI API. ServiceUI blev de facto standard måde at knytte brugergrænseflader til Jini services, og var den første Jini community standard godkendt via Jini beslutningsproces. Bill fungerer også som et valgt medlem af Jini-samfundets indledende tekniske tilsynsudvalg (TOC) og hjalp i denne rolle med at definere styringsprocessen for samfundet. Han bruger i øjeblikket det meste af sin energi på at bygge Artima.com til en stadig mere nyttig ressource for udviklere.