Approach 1: Test Geen Private Methods

Testing Private Methods with JUnit and SuiteRunner
by Bill Venners
May 24, 2004
samenvatting

dit artikel vergelijkt vier verschillende benaderingen voor het testen van private methoden in Java-klassen.

mijn allereerste gebruik van JUnit was om een conformiteitstestkit te bouwen voor de ServiceUI API . Het doel van een conformiteitstestkit is om ervoor te zorgen dat alternatieve implementaties van dezelfde API compatibel zijn met de specificatie van de API. Omdat een API-specificatie definieert alleen de openbare interface van de API, niet de implementatie van de API, een conformiteit test oefeningen alleen de openbare interface. Met andere woorden, een conformiteitstest is een “black box” – test. Het behandelt de API te testen als een black box, waarvan de externe interface kan worden gezien, maar waarvan de interne implementatie niet. Een conformiteitstest van een Java API, daarom, hoeft alleen toegang tot de publieke leden van de pakketten en klassen te testen. Er is geen noodzaak om toegang te krijgen tot package-level, protected, of private leden.

toen ik later JUnit toepaste op de taak van het schrijven van werkelijke eenheidstests, in tegenstelling tot conformiteitstests, merkte ik dat ik white box tests wilde schrijven—tests die kennis van de interne implementatie van de te testen pakketten en klassen gebruiken. Terwijl ik alleen publieke methoden wilde testen in mijn conformiteitstests, wilde ik unit tests schrijven voor pakkettoegang en af en toe private methoden en publieke methoden.

Daniel Steinberg toonde me de gangbare JUnit-techniek van het gebruik van parallelle broncodebomen, waardoor ik testklassen in hetzelfde pakket kon plaatsen als de te testen klassen, maar ze in een andere directory kon bewaren. Dit leverde een schone scheiding van test-en productiecode op. Door het plaatsen van beide bron bomen in de CLASSPATH, mijn test klassen kon toegang tot pakket-niveau methoden en klassen in het pakket te testen. Dit liet me echter nog steeds met het probleem van het testen van particuliere methoden.

toen ik Daniel vroeg over het testen van private methoden, stelde hij voorzichtig voor dat ik de private methoden indirect test door het testen van de package-access en publieke methoden die de private methoden noemen. Dit antwoord bevredigde me niet helemaal, want soms voelde ik echt de drang om een privémethode direct te testen. Mijn eerste oplossing was om gewoon dergelijke private methoden package toegang, die me in staat stelde om ze direct te testen met JUnit van de test klassen in hetzelfde pakket in de parallelle source tree. Dit werkte prima, maar ik voelde me een beetje vies op de een of andere manier. Hoewel ik over het algemeen ontdekte dat het denken over het ontwerpen van interfaces, zodat ze gemakkelijk unit getest konden worden, me hielp om betere interfaces te ontwerpen, voelde ik me in dit geval dat ik het ontwerp iets slechter maakte om het testbaar te maken.Toen ik later deelnam aan de creatie van Wat Frank Sommers, Matt Gerrans en ik uiteindelijk uitgaf als Artima SuiteRunner, zwoer ik dat ik het testen van private methoden gemakkelijker zou maken in SuiteRunner dan in JUnit. Maar na het onderzoeken van de verschillende benaderingen van het testen van private methoden, heb ik besloten om niets speciaals te doen in SuiteRunner om het testen van private methoden te ondersteunen. Dus of je nu gebruik maakt van JUnit of SuiteRunner, je hebt dezelfde vier basisbenaderingen voor het testen van private methoden:

  • test geen privé-methoden.
  • geef het methods package toegang.
  • gebruik een geneste testklasse.
  • gebruik reflectie.

In dit artikel zal ik deze vier benaderingen van het testen van private methoden in Java bespreken. Ik zal kijken naar de voor-en nadelen van elk en proberen om enig licht te werpen op wanneer het zinvol is om elke aanpak te gebruiken.

zoals ik in de inleiding al zei, hoorde ik voor het eerst het advies van Daniel Steinberg om af en toe mijn drang om private methoden te testen te onderdrukken. Maar Daniel is niet alleen de bron van dit advies dat ik ben tegengekomen. Het lijkt een gemeenschappelijke houding in de Java-gemeenschap. Bijvoorbeeld, de JUnit FAQ stelt:

het testen van private methoden kan een indicatie zijn dat deze methoden moeten worden verplaatst naar een andere klasse om hergebruik te bevorderen.

Charles Miller gaf een soortgelijk standpunt in zijn weblog :

als u een uitgebreide reeks tests hebt voor de blootgestelde (niet-privé) interface van een klasse, moeten deze tests, door hun aard, controleren of elke privémethode binnen de klasse ook werkt. Als dit niet het geval is, of als je een privémethode hebt die zo complex is dat het buiten de context van zijn publieke bellers moet worden getest, zou ik dat als een code-geur beschouwen.

en Dave Thomas en Andy Hunt , in hun boek Pragmatic Unit Testing, schrijven:

in het algemeen wil je geen inkapseling breken omwille van het testen (of zoals Mam altijd zei, “laat je privé niet bloot!”). De meeste van de tijd, je moet in staat zijn om een klasse te testen door de uitoefening van de openbare methoden. Als er significante functionaliteit is verborgen achter privé of beveiligde toegang, kan dat een waarschuwing zijn dat er een andere klasse is die moeite heeft om eruit te komen.

ik geloof in al dit advies. Meestal, private methoden kunnen het meest effectief worden getest via benadering 1, indirect door het testen van het pakket-niveau, beschermd, en openbare methoden die ze noemen. Maar onvermijdelijk zullen sommige mensen in sommige situaties het gevoel hebben dat het direct testen van een privémethode het juiste is om te doen.

in mijn geval heb ik de neiging om veel private utility methoden te maken. Deze hulpprogramma methoden doen vaak niets met bijvoorbeeld gegevens, ze werken gewoon op de doorgegeven parameters en retourneren een resultaat. Ik maak dergelijke methoden om de oproepende methode gemakkelijker te begrijpen. Het is een manier om de complexiteit van de implementatie van de klasse te beheren. Nu, als ik haal de private methode uit een methode die al werkt en heeft een goede unit test dekking, dan zullen die bestaande unit tests waarschijnlijk volstaan. Ik hoef niet meer unit tests te schrijven alleen voor de privé methode. Maar als ik de private methode wil schrijven voor de aanroepende methode, en Ik wil de unit tests schrijven voordat ik de private methode schrijf, dan wil ik de private methode direct testen. In het geval van Private utility methoden, Ik voel niet mijn drang om direct te testen van de methoden is, zoals de JUnit FAQ zet het, ” een indicatie dat deze methoden moeten worden verplaatst naar een andere klasse om hergebruik te bevorderen.”Deze methoden zijn eigenlijk alleen nodig in de klasse waarin ze wonen, en in feite worden ze vaak alleen genoemd door een andere methode.

een andere reden waarom ik soms de drang voel om privémethoden rechtstreeks te testen, is dat ik geneigd ben te denken dat unit testing mij helpt een robuust systeem te bereiken door dat systeem uit robuuste onderdelen te bouwen. Elk deel is een ” eenheid “waarvoor ik” eenheidstests kan schrijven.”De unit tests helpen me om ervoor te zorgen dat elke unit correct functioneert, wat me op zijn beurt helpt om een systeem te bouwen dat als geheel correct functioneert. De primaire eenheid die ik denk in termen van wanneer programmeren in Java is de klasse. Ik Bouw systemen uit klassen, en unit tests geven me het vertrouwen dat mijn klassen robuust zijn. Maar tot op zekere hoogte voel ik ook hetzelfde over de private methoden waaruit ik pakket samenstel-access, protected, en public methods. Deze private methoden zijn eenheden die individueel kunnen worden getest. Dergelijke unit tests geven me het vertrouwen dat de private methoden correct werken, die me helpt bouwen pakket-toegang, beschermd, en openbare methoden die robuust zijn.

zoals ik in de inleiding al zei, was het geven van methods package access mijn eerste benadering van het testen van private methoden met JUnit. Deze aanpak werkt eigenlijk prima, maar het komt met een lichte kosten. Als ik zie een private access specifier op een methode, het vertelt me iets wat ik graag wil weten—dat dit deel uitmaakt van de implementatie van de klasse. Ik weet dat ik de methode kan negeren als ik gewoon probeer om de klas van een andere klas in het pakket te gebruiken. Ik zou dit kunnen achterhalen over een package-access methode Door beter te kijken naar de naam, documentatie en code van de methode, maar het woord private communiceert dit veel efficiënter. Bovendien is mijn grootste probleem met deze benadering filosofisch. Hoewel ik het niet erg “breken encapsulation omwille van het testen,” zoals Dave en Andy zou het zeggen, Ik voel me gewoon niet goed over het breken van encapsulation op een manier die het pakket-niveau API verandert. Met andere woorden, hoewel ik heel enthousiast om niet-openbare methoden van de klassen te testen, dat wil zeggen, om te maken “white-box” unit tests, ik zou liever de API van de klassen te testen, met inbegrip van het pakket-niveau API, niet worden gewijzigd om deze tests te vergemakkelijken.

benadering 3: Gebruik een geneste testklasse

een derde methode voor het testen van particuliere methoden is het nestelen van een statische testklasse binnen de geteste productieklasse. Gezien het feit dat een geneste klasse toegang heeft tot de private leden van zijn omsluitende klasse, zou het in staat zijn om de private methoden direct aan te roepen. De statische klasse zelf kan pakkettoegang zijn, waardoor het kan worden geladen als onderdeel van de white box-test.

het nadeel van deze aanpak is dat als je niet wilt dat de geneste test klasse toegankelijk is in je deployment JAR Bestand, je een beetje extra werk moet doen om het uit te pakken. Ook, sommige mensen misschien niet graag met testcode gemengd in hetzelfde bestand als productiecode, hoewel anderen kunnen de voorkeur aan die aanpak.

benadering 4: Gebruik reflectie

de vierde benadering van het testen van private methoden werd mij voorgesteld door Vladimir R. Bossicard, die JUnit Addons schreef . Op een dag tijdens de lunch, Vladimir vertelde me dat de java.lang.reflect API bevatte methoden die client code om toegang bescherming mechanisme van de Java virtuele machine te omzeilen. Hij vertelde me ook dat zijn JUnit Addons project een klasse bevatte, junitx.util.PrivateAccessor, om te helpen bij het gebruik van de reflectie API voor dit doel: om unit tests te schrijven die private leden van de te testen klassen manipuleren. De JUnit FAQ verwijst naar een soortgelijke klasse, genaamd PrivilegedAccessor , geschreven door Charlie Hubbard en Prashant Dhotke.

een voordeel van het gebruik van de reflectiebenadering bij het testen van particuliere methoden is dat het een schone scheiding van testcode en productiecode biedt. De tests hoeven niet in de te testen klasse te worden genest, zoals in nadering 3. Integendeel, ze kunnen worden geplaatst naast de andere tests die het pakket-niveau en openbare methoden van de klasse uit te oefenen. Bovendien hoeft u de API van de testklasse niet te wijzigen. In tegenstelling tot benadering 2, kunnen particuliere methoden privé blijven. In tegenstelling tot benadering 3, hoeft u geen extra geneste klasse toe te voegen op package access level. Het belangrijkste nadeel van deze aanpak is dat de test code is veel uitgebreider omdat het gebruik maakt van de reflectie API. Bovendien, refactoring IDEs zoals Eclipse en IntelliJ zijn meestal niet zo bedreven in het veranderen van de namen van methoden waar ze worden aangeduid als Strings doorgegeven aan de methoden van de reflectie API. Dus als u de naam van de privé-methode met uw refactoring IDE wijzigt, moet u mogelijk nog enkele wijzigingen met de hand aanbrengen in de testcode.

een voorbeeld

om een voorbeeld te geven van een particuliere methode die naar mijn mening direct unit testing verdient, heb ik wat functionaliteit uit demainmethode van klasseorg.suiterunner.Runnergehaald.Runner.mainontleedt commandoregelargumenten en voert een reeks tests uit, waarbij optioneel de GUI wordt opgestart. De methode die ik geëxtraheerd heb,

parseArgsIntoLists, doet een deel van het werk van het ontleden van de opdrachtregelargumenten naar de toepassing SuiteRunner. Nu, om de publieke methode die deze private methode noemt te testen, zou ikmainmoeten testen. Belangrijkste, natuurlijk, is de gehele toepassing, waardoor de methode vrij moeilijk te testen. In feite heb ik geen bestaande test voormain.

op dit punt vraagt u zich misschien af, als ik eerst tests schreef in de stijl van testgestuurde ontwikkeling, hoe heb ik dan ooit parseercode geschreven die geen eenheidstests had? De belangrijkste reden is dat mijn test infectie is gekomen in fasen. Ik heb eigenlijk gevangen een eenheid test griep lang voordat ik had gehoord van JUnit of lezen Test geïnfecteerd . Terug toen ik Windows-toepassingen in C++ bouwde, bijvoorbeeld, zou ik een beetje code schrijven om een nieuw geà mplementeerde methode te testen, dan voert u die code uit en kijkt u hoe deze wordt uitgevoerd door de testmethode met de debugger te doorlopen. Dit soort unit testen hielp me robuustheid te bereiken, maar de tests zelf niet controleren op het juiste gedrag. Ik heb zelf gecontroleerd op het juiste gedrag door te observeren via de debugger. De tests waren niet geautomatiseerd, en daarom heb ik ze niet opgeslagen zodat ze later opnieuw kunnen worden uitgevoerd. Toen ik Test Infected las, zag ik meteen de waarde van het automatiseren van de tests en ze te houden als een soort regressietest na refactoring, maar voor een lange tijd had ik geen zin om de tests eerst te schrijven. Ik wilde de tests schrijven nadat ik de functionaliteit had geïmplementeerd, want dat was toen ik de tests met de debugger had uitgevoerd. Een secundaire reden waarom ik niet eerst testen schreef tijdens het ontwikkelen van veel van SuiteRunner is dat ik de tests van SuiteRunner zelf wilde schrijven, in een poging om mijn eigen hondenvoer te eten. Tot SuiteRunner ‘ s basic API geregeld, Ik had niet de testtoolkit die ik wilde gebruiken om de tests te schrijven.

sinds die tijd heeft het testvirus echter een sterkere greep op mij gekregen, en ik geef er nu de voorkeur aan om meestal eerst unit tests te schrijven. Ik geef de voorkeur aan het schrijven van tests eerst niet zozeer omdat ik vind ik uiteindelijk met schonere ontwerpen, die meestal wordt gepromoot als het belangrijkste voordeel van test-driven ontwikkeling. Liever, Ik geef de voorkeur aan het schrijven van tests eerst, omdat ik vind dat vaak als ik duik in de code onder druk, met de bedoeling dat ik de test later te schrijven, de test eigenlijk nooit wordt geschreven. SuiteRunner zelf heeft zeer weinig unit tests op dit punt om die reden. Hier is de parseArgsIntoLists methode:

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

de opdrachtregel voor SuiteRunner bevat drie soorten informatie die door SuiteRunner wordt gebruikt om tests uit te voeren: runpath, reporters en suites. De parseArgsIntoLists methode gaat alleen door de argumenten die worden doorgegeven als een array van String s, en plaatst elk argument in een van de lijsten, runpathList, reportersList, en suitesList.

voordat ik een test schrijf voor deze privémethode, zou ik willen vragen of mijn drang om deze unit test te schrijven een code geur vertegenwoordigt, zoals Charles Miller het in zijn weblog schreef? Geeft het aan dat parseArgsIntoLists moet worden verplaatst naar een andere klasse om hergebruik te bevorderen, zoals de JUnit FAQ suggereert? Zouden Dave en Andy zeggen dat het een waarschuwing is dat er nog een klas is die moeite heeft om eruit te komen? Nou, misschien. Ik zou een ArgumentsParser klasse kunnen maken die slechts een paar statische methoden bevat die het parseerwerk uitvoeren. Zowel de ArgumentsParser klasse als de methoden die het bevat zouden pakkettoegang kunnen zijn, waardoor ze gemakkelijk te testen zijn. Maar dat voelt niet goed voor mij. Deze methoden worden alleen door Runner.mainaangeroepen. Ze voelen duidelijk als privé methoden voor mij. De enige reden dat ik ze zou verplaatsen naar een ArgumentsParser klasse is om ze te kunnen testen. Ik zou in feite gebruik maken van aanpak nummer 2: Maak de private methoden pakket toegang.

in plaats daarvan besloot ik voor dit voorbeeld benadering nummer 4 te nemen en reflectie te gebruiken. Ik keek zowel naar Vladimir Bossicard ’s junitx.utils.PrivateAccessor en Charlie Hubbard en Prashant Dhotke’ s PrivilegedAccessor, maar besloot dat geen van beide me hielp zoals ik wilde. Voor een ding, deze klassen hebben beide de mogelijkheid om velden te testen om ervoor te zorgen dat ze correct zijn ingesteld. Tot nu toe heb ik nog nooit een drang gevoeld om direct toegang te krijgen tot privévelden van unit tests. Ik wil gewoon in staat zijn om private utility methoden te testen. Het grootste probleem dat ik had met deze twee klassen, echter, is hoe ze omgaan met de uitzonderingen die kunnen worden gegooid wanneer het proberen om de private methode te roepen via reflectie. Elke klasse heeft een of meer methoden waarvan de taak het aanroepen van een methode met reflectie. PrivilegedAccessor ‘ s twee invokeMethod methoden geeft elke uitzondering terug aan de beller, inclusief drie gecontroleerde uitzonderingen die zijn aangegeven in de plaids-clausule: NoSuchMethodException, IllegalAccessException, en InvocationTargetException. PrivateAccessor’s twee invoke methoden vangen InvocationTargetException, en extraheren en gooien de doeluitzondering, de werkelijke uitzondering die door de aangeroepen methode wordt gegooid. Het vangt dan elke andere uitzondering en gooit NoSuchMethodException. Ik vond het niet leuk dat de beller van PrivilegedAccessor.invokeMethod altijd de drie gecontroleerde uitzonderingen zou moeten behandelen, omdat ik dacht dat de algemene manier om elke uitzondering te behandelen zou zijn om de test te laten mislukken. Ik was ook bezorgd dat de PrivateAccessor.invoke potentieel nuttige stack trace informatie weggooide in zijn exception handling policy. Wat ik echt wilde was een methode die probeerde een private methode met reflectie aan te roepen, die elke gegooid uitzondering naast InvocationTargetException in een ongecontroleerd TestFailedExceptionwikkelde. Meestal zou deze uitzondering ervoor zorgen dat de test mislukt. In tests waarvan werd verwacht dat er een uitzondering zou worden gemaakt, kon de uitzondering in InvocationTargetException worden geëxtraheerd en op juistheid worden getest.

daarom schreef ik invokeStaticMethod. De setAccessible(true) aanroep maakt het mogelijk om de private methode van buiten de klasse aan te roepen. Een overeenkomstige invokeStaticMethod implementatie voor gebruik met JUnit zou AssertionFailedError in plaats van TestFailedExceptiongooien. Hier is de code:

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

volgende, Ik creëerde een gemak methode die de bijzondere particuliere methode die ik wilde testen oproept:

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

eindelijk kon ik testen schrijven tegen de private methode zonder teveel rommel, zoals dit:

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

conclusie

benadering 1, het indirect testen van particuliere methoden door het testen van de pakketniveau, beschermde en publieke methoden die ze noemen, zal vaak de beste aanpak zijn. In gevallen waar u echt privémethoden rechtstreeks wilt testen, biedt het gebruik van reflectie om privémethoden te testen, hoewel nogal omslachtig, de schoonste scheiding van testcode van productiecode en de minste impact op productiecode. Echter, als je het niet erg om die specifieke private methoden die u wilt testen pakket Toegang, je zou kunnen gebruiken aanpak 2. Of als je het niet erg het plaatsen van een geneste test klasse in uw productie klasse te testen, benadering 3 zou op zijn minst laat u de private methoden privé te houden.

er is geen perfect antwoord. Maar als je benadering 4 aanneemt, zal je uiteindelijk eindigen met een handvol methoden zoals invokeStaticMethod die je kunt hergebruiken. Als je eenmaal een convenience method schrijft, zoals invokeParseArgsIntoLists, voor een private method, kun je zonder veel moeite testen tegen de private method schrijven.

middelen

1. De ServiceUI API definieert een standaard manier om gebruikersinterfaces te koppelen aan Jini services:
http://www.artima.com/jini/serviceui/index.html

2. Daniel Steinberg is momenteel hoofdredacteur van Java.NET:
http://www.java.net/

3. Artima SuiteRunner is een gratis open source testing toolkit en JUnit runner:
http://www.artima.com/suiterunner/index.html

4.JUnit FAQ Vraag over het testen van private methoden:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10

5. Testing Private Methods (Don ‘ t Do It), Een weblog post door Charles Miller:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it

6. Andy Hunt en Dave Thomas zijn de auteurs van Pragmatic Unit Testing, die verkrijgbaar is in de Pragmatic Store.

7. JUnit Addons is een verzameling van helper klassen voor JUnit gemaakt door Vladimar R. Bossicard:
http://sourceforge.net/projects/junit-addons

PrivateAccessor is de klasse van JUnit Addons die het testen van private leden faciliteert:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html

9.Privigedaccessor class, die u kunt gebruiken om toegang te krijgen tot privé-leden:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java

10. Test Driven Development by Example, door Kent Beck, beschrijft de test-first techniek:
http://www.amazon.com/exec/obidos/ASIN/0321146530/

11. Test Besmet, van Kent Beck en Erich Gamma, geïntroduceerd JUnit de wereld:
http://junit.sourceforge.net/doc/testinfected/testing.htm

Unit Testing Eigen Methoden, een weblog bericht over nUnit door Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx

Ondermijnt de Java Access Protection Unit Testing, een O ‘ Reilly OnJava.com artikel door Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html

Class RunnerSuite, van waaruit de code fragmenten in dit artikel zijn genomen, verschijnt er in zijn geheel hier:
http://www.artima.com/suiterunner/privateExample.html

Waarom We Refactoring van JUnit
http://www.artima.com/suiterunner/why.html

Artima SuiteRunner Tutorial, Gebouw-Conformiteit en Unit Tests met Artima SuiteRunner:
http://www.artima.com/suiterunner/tutorial.html

Aan de Slag met Artima SuiteRunner, het Uitvoeren van de Eenvoudige Voorbeeld Opgenomen in de Distributie:
http://www.artima.com/suiterunner/start.html

Runnning JUnit Tests met Artima SuiteRunner, hoe het te gebruiken Artima SuiteRunner als een JUnit loper voor het uitvoeren van uw bestaande JUnit test suites:
http://www.artima.com/suiterunner/junit.html

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

Artima SuiteRunner download pagina (U moet zich aanmelden op Artima.com downloaden van de release):
http://www.artima.com/suiterunner/download.jsp

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

terug te Praten!

een advies hebben? Lezers hebben al 26 reacties geplaatst over dit artikel. Waarom die van jou niet toevoegen?

over de auteur

Bill Venners is president van Artima Software, Inc. en hoofdredacteur van Artima.com. hij is auteur van het boek, Inside The Java Virtual Machine, een programmer-georiënteerd overzicht van de architectuur en de binnenkant van het Java platform. Zijn populaire columns in JavaWorld gingen over Java internals, object-oriented design en Jini. Bill is sinds haar oprichting actief in de Jini-gemeenschap. Hij leidde het ServiceUI-project van de Jini-Gemeenschap dat de ServiceUI API produceerde. De ServiceUI werd de de facto standaard manier om gebruikersinterfaces te koppelen aan Jini-diensten, en was de eerste Jini community-standaard die werd goedgekeurd via het Jini-besluitvormingsproces. Bill fungeert ook als een gekozen lid van de Jini-gemeenschap initial Technical Oversight Committee (TOC), en in deze rol hielp bij het definiëren van het bestuursproces voor de gemeenschap. Hij besteedt momenteel het grootste deel van zijn energie aan het bouwen Artima.com in een steeds nuttiger bron voor ontwikkelaars.

You might also like

Geef een antwoord

Het e-mailadres wordt niet gepubliceerd.