megközelítés 1: ne tesztelje privát módszerek

tesztelés privát módszerek JUnit és SuiteRunner
Bill Venners
május 24, 2004
összefoglaló

ez a cikk négy különböző megközelítést hasonlít össze a Java osztályok magán metódusainak tesztelésére.

a JUnit legelső használata egy megfelelőségi tesztkészlet elkészítése volt a ServiceUI API-hoz . A megfelelőségi tesztkészlet célja annak biztosítása, hogy ugyanazon API alternatív implementációi kompatibilisek legyenek az API specifikációjával. Mivel az API specifikáció csak az API nyilvános felületét határozza meg, az API megvalósítását nem, a megfelelőségi teszt csak a nyilvános felületet gyakorolja. Más szavakkal, a megfelelőségi teszt egy “fekete doboz” teszt. A vizsgált API-t fekete dobozként kezeli, amelynek külső felülete látható, de belső megvalósítása nem. A Java API megfelelőségi tesztjének ezért csak a tesztelt csomagok és osztályok nyilvános tagjaihoz kell hozzáférnie. Nincs szükség csomagszintű hozzáférésre, védett, vagy privát tagok.

amikor később a JUnit—ot alkalmaztam a tényleges egységtesztek megírására, szemben a megfelelőségi tesztekkel, azon kaptam magam, hogy fehér dobozos teszteket akarok írni-olyan teszteket, amelyek a vizsgált csomagok és osztályok belső megvalósításának ismeretét alkalmazzák. Míg én csak a nyilvános módszereket akartam tesztelni a megfelelőségi tesztjeimben, egységteszteket akartam írni a csomaghoz való hozzáféréshez és esetenként magán metódusokhoz, valamint nyilvános módszerekhez.

Daniel Steinberg megmutatta nekem a párhuzamos forráskód fák használatának közös JUnit technikáját, amely lehetővé tette számomra, hogy a tesztosztályokat ugyanabba a csomagba helyezzem, mint a vizsgált osztályokat, de más könyvtárban tartsam őket. Ez lehetővé tette a teszt és a gyártási kód egyértelmű elválasztását. Azáltal, hogy mindkét forrásfát az OSZTÁLYÚTVONALBA helyezi, a tesztosztályaim elérhetik a csomagszintű metódusokat és osztályokat a vizsgált csomagban. Ez azonban továbbra is a magán módszerek tesztelésének problémáját hagyta bennem.

amikor megkérdeztem Danielt a privát módszerek teszteléséről, gyengéden azt javasolta, hogy teszteljem a privát módszereket közvetett módon a csomag tesztelésével-hozzáférés és nyilvános módszerek, amelyek a privátokat hívják. Ez a válasz nem elégített ki engem, mert alkalmanként valóban éreztem a késztetést egy privát módszer közvetlen tesztelésére. A kezdeti megoldás az volt, hogy csak az ilyen privát módszerek csomag hozzáférés, amely lehetővé tette számomra, hogy teszteljék őket közvetlenül JUnit a teszt osztályok ugyanabban a csomagban a párhuzamos forrásfa. Ez jól működött, de valahogy kissé piszkosnak éreztem magam. Bár általában rájöttem, hogy az interfészek tervezésének gondolkodása, hogy azok könnyen egységtesztelhetők legyenek, segített a jobb interfészek tervezésében, ebben az esetben úgy éreztem, hogy a tervezést kissé rosszabbá teszem, hogy tesztelhetővé tegyem.

amikor később részt vettem a what frank Sommers, Matt Gerrans, és végül artima SuiteRunner néven jelent meg , megfogadtam, hogy megkönnyítem a privát módszerek tesztelését a SuiteRunner-ben, mint a JUnit-ban. De miután megvizsgáltam a magán módszerek tesztelésének különféle megközelítéseit, úgy döntöttem, hogy a SuiteRunner-ben Nem teszek semmi különöset a magán módszerek tesztelésének támogatására. Tehát függetlenül attól, hogy Junitot vagy Suiterunnert használ, ugyanaz a négy alapvető megközelítés van a privát módszerek tesztelésére:

  • ne tesztelje a privát módszereket.
  • adja meg a módszerek csomaghoz való hozzáférését.
  • beágyazott tesztosztály használata.
  • használja a reflexiót.

ebben a cikkben a privát módszerek Java-ban történő tesztelésének négy megközelítését fogom megvitatni. Megvizsgálom mindegyik előnyeit és hátrányait, és megpróbálok fényt deríteni arra, hogy mikor van értelme az egyes megközelítéseket használni.

mint a bevezetőben említettem, először Daniel Steinbergtől hallottam azt a Tanácsot, hogy elnyomjam az alkalmi késztetéseimet a magán módszerek tesztelésére. De Daniel nem csak a forrása ennek a Tanácsnak, amellyel találkoztam. Úgy tűnik, hogy ez egy általános hozzáállás a Java közösségben. Például a JUnit GYIK kimondja:

a privát módszerek tesztelése jelezheti, hogy ezeket a módszereket át kell helyezni egy másik osztályba az újrafelhasználhatóság elősegítése érdekében.

Charles Miller hasonló álláspontot fogalmazott meg weblogjában :

ha alapos tesztcsomaggal rendelkezik egy osztály kitett (nem privát) felületéhez, akkor ezeknek a teszteknek természetüknél fogva ellenőrizniük kell, hogy az osztályon belüli privát módszerek is működnek-e. Ha nem ez a helyzet, vagy ha van egy privát módszer olyan összetett, hogy meg kell vizsgálni ki az összefüggésben a nyilvános hívók, azt hiszem, hogy egy kód-szag.

és Dave Thomas és Andy Hunt, könyvükben Pragmatic Unit Testing, write:

általában nem akarja megtörni a kapszulázást a tesztelés érdekében (vagy ahogy Anya szokta mondani: “ne tegye ki a közlegényeit!”). Az idő nagy részében, képesnek kell lennie arra, hogy teszteljen egy osztályt a nyilvános módszereinek gyakorlásával. Ha van olyan jelentős funkció, amely a privát vagy védett hozzáférés mögött rejtőzik, ez figyelmeztető jel lehet arra, hogy van egy másik osztály, amely küzd a kijutásért.

azt hiszem, minden ezt a tanácsot. A legtöbb esetben a magán metódusokat az 1. megközelítéssel lehet a leghatékonyabban tesztelni, közvetett módon az őket meghívó csomagszintű, védett és nyilvános metódusok tesztelésével. De elkerülhetetlenül néhány ember bizonyos helyzetekben úgy fogja érezni, hogy a privát módszer közvetlen tesztelése a helyes dolog.

az én esetemben sok privát segédprogramot hozok létre. Ezek a segédprogramok gyakran semmit sem tesznek a példányadatokkal, csak az átadott paramétereken működnek, és eredményt adnak vissza. Olyan módszereket hozok létre, amelyek megkönnyítik a hívási módszer megértését. Ez egy módja az osztály megvalósításának összetettségének kezelésére. Most, ha kivonom a privát módszert egy olyan módszerből, amely már működik és jó egységteszt lefedettséggel rendelkezik, akkor a meglévő egységtesztek valószínűleg elegendőek. Nem kell több egységtesztet írnom csak a privát módszerhez. De ha meg akarom írni a privát metódust a hívási metódus előtt, és meg akarom írni az egységteszteket a privát metódus írása előtt, akkor visszatérek ahhoz, hogy közvetlenül teszteljem a privát metódust. Magánhasznú módszerek esetében, nem érzem késztetésemet a módszerek közvetlen tesztelésére, ahogy a JUnit GYIK fogalmazott, ” annak jelzése, hogy ezeket a módszereket át kell helyezni egy másik osztályba az újrafelhasználhatóság elősegítése érdekében.”Ezekre a módszerekre valójában csak abban az osztályban van szükség, amelyben laknak, és valójában gyakran csak egy másik módszerrel hívják őket.

egy másik ok, amiért néha késztetést érzek a privát módszerek közvetlen tesztelésére, az az, hogy hajlamos vagyok az egységtesztelésre úgy gondolni, hogy segít elérni egy robusztus rendszert azáltal, hogy ezt a rendszert robusztus alkatrészekből építem fel. Minden rész egy “egység”, amelyre “egységteszteket” tudok írni.”Az egységtesztek segítenek abban, hogy minden egység megfelelően működjön, ami viszont segít egy olyan rendszer felépítésében, amely egészében megfelelően működik. Az elsődleges egység, amelyet a Java programozásakor gondolok, az osztály. Rendszereket építek az osztályokból, és az egységtesztek bizalmat adnak nekem abban, hogy az osztályaim robusztusak. De bizonyos mértékig ugyanígy érzek a privát módszerekkel kapcsolatban is, amelyekből csomagot készítek-hozzáférés, védett és nyilvános módszerek. Ezek a magán módszerek olyan egységek, amelyek egyedileg tesztelhetők. Az ilyen egységtesztek bizalmat adnak nekem abban, hogy a privát módszerek megfelelően működnek, ami segít a csomag-hozzáférés, a védett és a nyilvános módszerek felépítésében, amelyek robusztusak.

mint a bevezetőben említettem, a módszerek csomaghozzáférésének megadása volt az első megközelítésem a privát módszerek teszteléséhez a JUnit segítségével. Ez a megközelítés valóban jól működik, de enyhe költségekkel jár. Amikor egy privát hozzáférési specifikátort látok egy módszeren, azt mondja nekem, amit szeretek tudni—hogy ez az osztály megvalósításának része. Tudom, hogy figyelmen kívül hagyhatom a módszert, ha csak egy másik osztály osztályát próbálom használni a csomagban. Ezt egy csomag-hozzáférési módszerről ki tudnám deríteni, ha közelebbről megvizsgálnám a módszer nevét, dokumentációját és kódját, de a privát szó ezt sokkal hatékonyabban kommunikálja. Sőt, a fő problémám ezzel a megközelítéssel filozófiai. Bár nem bánom “breaking kapszulázás kedvéért tesztelés,” ahogy Dave és Andy azt mondta, Csak nem érzem jól magam törés kapszulázás oly módon, hogy megváltoztatja a csomag szintű API. Más szavakkal, bár nagyon lelkes vagyok az osztályok nem nyilvános módszereinek tesztelésére, azaz “fehér dobozos” egységtesztek létrehozására, inkább a vizsgált osztályok API-ját, beleértve a csomagszintű API-t is, nem változtatják meg a tesztek megkönnyítése érdekében.

3.megközelítés: beágyazott Tesztosztály használata

a privát módszerek tesztelésének harmadik megközelítése egy statikus tesztosztály beágyazása a vizsgált termelési osztályba. Tekintettel arra, hogy egy beágyazott osztály hozzáfér a zárt osztály privát tagjaihoz, képes lenne közvetlenül meghívni a privát metódusokat. Maga a statikus osztály lehet csomaghozzáférés, lehetővé téve annak betöltését a fehér doboz teszt részeként.

ennek a megközelítésnek az a hátránya, hogy ha nem akarja, hogy a beágyazott tesztosztály elérhető legyen a telepítési JAR fájlban, akkor egy kis extra munkát kell végeznie annak kibontásához. Néhány embernek nem tetszik, ha a tesztkód ugyanabban a fájlban keveredik, mint a gyártási kód, bár mások inkább ezt a megközelítést részesítik előnyben.

4 .megközelítés: használja a reflexiót

a privát módszerek tesztelésének negyedik megközelítését Vladimir R. Bossicard javasolta nekem, aki JUnit kiegészítőket írt. Egy nap ebéd közben Vlagyimir felvilágosított, hogy a java.lang.reflect API olyan módszereket tartalmazott, amelyek lehetővé tették az ügyfélkód megkerülését hozzáférés-védelmi mechanizmus a Java virtuális gép. Azt is elmondta, hogy a JUnit Addons projektje tartalmazott egy osztályt, junitx.util.PrivateAccessor, hogy segítse a reflection API használatát éppen erre a célra: olyan egységtesztek írására, amelyek manipulálják a vizsgált osztályok magán tagjait. A JUnit GYIK egy hasonló osztályra mutat, az úgynevezett PrivilegedAccessor , amelyet Charlie Hubbard és Prashant Dhotke írt.

a reflexiós megközelítés egyik előnye a magán módszerek tesztelésében az, hogy tiszta elválasztást biztosít a tesztkód és a gyártási kód között. A teszteket nem kell beágyazni a vizsgált osztályba, mint a 3. megközelítésben. Inkább elhelyezhetők a többi teszt mellett, amelyek az osztály csomagszintű és nyilvános módszereit gyakorolják. Ezenkívül nem kell megváltoztatnia a vizsgált osztály API-ját. A 2. megközelítéssel ellentétben a privát módszerek privátak maradhatnak. A 3. megközelítéstől eltérően nem kell további beágyazott osztályt hozzáadnia a csomaghozzáférési szinten. Ennek a megközelítésnek az a fő hátránya, hogy a tesztkód sokkal bőbeszédűbb, mert a reflection API-t használja. Ezenkívül az olyan IDE-k refaktorálása, mint az Eclipse és az IntelliJ, általában nem olyan ügyes a metódusok nevének megváltoztatásában, ahol Strings-nek nevezik őket, átadva a reflection API metódusainak. Tehát, ha megváltoztatja a privát módszer nevét az IDE refaktorálásával, akkor még mindig kézzel kell módosítania a tesztkódot.

példa

egy példa egy olyan privát módszerre, amely véleményem szerint közvetlen egységtesztelést érdemel, kivontam néhány funkciót amainosztály módszerébőlorg.suiterunner.Runner.Runner.mainelemzi a parancssori argumentumokat, és futtat egy tesztcsomagot, opcionálisan a GUI-t. Az általam kibontott módszer,parseArgsIntoLists, része a parancssori argumentumok elemzésének a SuiteRunner alkalmazáshoz. Most, hogy teszteljem a nyilvános módszert, amely ezt a privát módszert hívja, tesztelnem kellmain. A fő természetesen a teljes alkalmazás, ami meglehetősen megnehezíti a módszer tesztelését. Valójában nincs létező tesztemmain– re.

ezen a ponton elgondolkodhat azon, hogy ha először tesztvezérelt fejlesztés stílusában írtam teszteket, hogyan tudtam végül olyan elemző kódot írni , amelynek nem volt egységtesztje? A fő ok az, hogy a tesztfertőzésem szakaszokban jött létre. Én valóban elkapni egy egység teszt influenza jóval azelőtt, hogy hallottam JUnit vagy olvasni teszt fertőzött . Amikor például Windows alkalmazásokat építettem C++ – ban, írtam egy kis kódot egy újonnan megvalósított módszer tesztelésére, majd végrehajtottam azt a kódot, és megnéztem, hogy végrehajtja-e a hibakeresővel tesztelt módszert. Ez a fajta egységteszt segített a robusztusság elérésében, de maguk a tesztek nem ellenőrizték a megfelelő viselkedést. A hibakeresőn keresztül megfigyelve magam ellenőriztem a megfelelő viselkedést. A tesztek nem voltak automatizáltak, ezért nem mentettem el őket, hogy később újra futtathassák őket. Amikor elolvastam a Test Infected-et, azonnal megláttam a tesztek automatizálásának értékét, és a refaktorálás után egyfajta regressziós tesztként tartottam őket, de sokáig nem volt értelme először a teszteket írni. A teszteket a funkcionalitás megvalósítása után akartam megírni, mert ekkor futtattam a teszteket a hibakeresővel. Másodlagos ok, amiért nem írtam először teszteket, miközben a SuiteRunner nagy részét fejlesztettem, az az, hogy a SuiteRunner tesztjeit magával a SuiteRunner-rel akartam írni, annak érdekében, hogy megegyem a saját kutyaeledelemet. Amíg a SuiteRunner alapvető API-ja nem rendeződött, nem volt meg a tesztelő eszközkészlet, amelyet használni akartam a tesztek megírásához.

azóta azonban a tesztelő vírus erősebb hatást gyakorolt rám, és most inkább az egységteszteket írom először. Először inkább teszteket írok, nem annyira, mert úgy találom, hogy tisztább tervekkel végzem, amelyet általában a tesztvezérelt fejlesztés fő előnyeként hirdetnek. Inkább inkább teszteket írok először, mert gyakran úgy találom, hogy ha nyomás alatt belemerülök a kódba, azzal a szándékkal, hogy később megírom a tesztet, a tesztet valójában soha nem írják meg. Maga a SuiteRunner ezen a ponton nagyon kevés egységteszttel rendelkezik éppen ezért. Itt van a parseArgsIntoLists módszer:

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

a SuiteRunner parancssora háromféle információt tartalmaz, amelyeket a SuiteRunner használ a tesztek futtatásához: runpath, riporterek és lakosztályok. A parseArgsIntoLists metódus csupán a Strings tömbként átadott argumentumokon megy keresztül, és minden argumentumot a runpathList, reportersList és suitesListlisták egyikébe helyez.

mielőtt írnék egy tesztet erre a privát módszerre, megkérdezném, hogy a késztetés, hogy ezt az egységtesztet írjam, kódszagot jelent-e, ahogy Charles Miller a weblogjában tette? Azt jelzi, hogy a parseArgsIntoLists – et át kell helyezni egy másik osztályba az újrafelhasználhatóság előmozdítása érdekében, amint azt a JUnit GYIK javasolja? Dave és Andy azt mondaná, hogy ez egy figyelmeztető jel, hogy egy másik osztály küzd a kijutásért? Nos, talán. Concievably létre tudok hozni egy ArgumentsParser osztályt, amely csak néhány statikus metódust tartalmaz, amelyek elvégzik az elemzési munkát. Mind a ArgumentsParser osztály, mind a benne található módszerek lehetnek csomaghozzáférés, ami megkönnyíti a tesztelést. De ezt nem érzem helyesnek. Ezeket a módszereket csak Runner.main hívja meg. Úgy érzem magam, mint egy privát módszer. Az egyetlen ok, amiért áthelyezném őket egy ArgumentsParser osztályba, az az, hogy tesztelhessem őket. Valójában a 2. számú megközelítést használnám: tegye hozzáférhetővé a privát módszerek csomagját.

ehelyett ebben a példában úgy döntöttem, hogy a 4-es megközelítést alkalmazom, és reflexiót használok. Megnéztem mind Vladimir Bossicard junitx.utils.PrivateAccessor, mind Charlie Hubbard és Prashant Dhotke PrivilegedAccessor című könyvét, de úgy döntöttem, hogy egyikük sem segített nekem annyira, ahogy akartam. Egyrészt ezek az osztályok képesek tesztelni a mezőket, hogy megbizonyosodjanak arról, hogy helyesen vannak-e beállítva. Még soha nem éreztem késztetést arra, hogy közvetlenül hozzáférjek a privát mezőkhöz az egységtesztekből. Csak azt akarom, hogy képes legyen tesztelni magán közüzemi módszerek. A fő probléma volt ezzel a két osztály, azonban, hogyan kezelték a kivételeket, hogy lehet dobni, amikor megpróbálja hivatkozni a privát módszer keresztül reflexió. Minden osztálynak van egy vagy több metódusa, amelynek feladata reflexióval hívja meg a metódust. Az PrivilegedAccessor két invokeMethod metódusa minden kivételt visszaad a hívónak, beleértve a throws záradékban deklarált három ellenőrzött kivételt: NoSuchMethodException, IllegalAccessExceptionés InvocationTargetException. Ezzel szemben a PrivateAccessor két invoke metódusa elkapja a InvocationTargetException – t, és kivonja és eldobja a célkivételt, a meghívott metódus által dobott tényleges kivételt. Ezután elkap minden más kivételt, és NoSuchMethodException – et dob. Nem tetszett, hogy a PrivilegedAccessor.invokeMethod hívójának mindig kezelnie kell a három ellenőrzött kivételt, mert úgy gondoltam, hogy a kivétel kezelésének általános módja az, ha hagyja, hogy a teszt sikertelen legyen. Aggódtam amiatt is, hogy a PrivateAccessor.invoke potenciálisan hasznos veremkövetési információkat dobott el kivételkezelési politikájában. Amit igazán akartam, az egy olyan módszer volt, amely megkísérelte meghívni egy privát módszert reflexióval, amely a InvocationTargetException mellett minden dobott kivételt egy bejelöletlen TestFailedException – be csomagolt. Legtöbbször ez a kivétel a teszt kudarcát okozza. Azokban a tesztekben, amelyek kivételre számítottak, a InvocationTargetException – ben szereplő kivétel kivonható és tesztelhető a helyesség szempontjából.

ezért írtam invokeStaticMethod. A setAccessible(true) hívás lehetővé teszi a privát metódus meghívását az osztályon kívülről. Egy megfelelő invokeStaticMethod megvalósítás a JUnit-hez való használatra AssertionFailedError – et dobna a TestFailedException helyett. Itt a kód:

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

következő, létrehoztam egy kényelmi módszert, amely meghívja azt a privát módszert, amelyet tesztelni akartam:

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

végre, tudtam írni tesztek ellen a privát módszer nem túl sok felesleges rendetlenséget, mint ez:

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

következtetés

az 1. megközelítés, a privát módszerek közvetett tesztelése a csomagszintű, védett és nyilvános módszerek tesztelésével, amelyek hívják őket, gyakran a legjobb megközelítés. Azokban az esetekben, amikor valóban közvetlenül szeretné tesztelni a magánmódszereket, a reflexió használata a magánmódszerek tesztelésére, bár meglehetősen nehézkes, biztosítja a tesztkód legtisztább elválasztását a gyártási kódtól, és a legkisebb hatást gyakorolja a gyártási kódra. Ha azonban nem bánja, hogy ezeket a privát módszereket tesztelje a csomaghoz való hozzáférést, használhatja a 2.megközelítést. Vagy ha nem bánja, hogy egy beágyazott tesztosztályt tesztel a gyártási osztályon belül, a 3. megközelítés legalább lehetővé tenné, hogy a privát módszereket magántulajdonban tartsa.

nincs tökéletes válasz. De ha elfogadja a 4. megközelítést, akkor végül egy maroknyi módszer lesz, például invokeStaticMethod, amelyeket újra felhasználhat. Miután írt egy kényelmi módszert, például invokeParseArgsIntoLists, egy privát módszerhez, sok nehézség nélkül írhat teszteket a privát módszerrel szemben.

források

1. A ServiceUI API meghatározza a felhasználói felületek Jini szolgáltatásokhoz való csatolásának szokásos módját:
http://www.artima.com/jini/serviceui/index.html

2. Daniel Steinberg jelenleg a főszerkesztő Java.NET:
http://www.java.net/

3. Artima SuiteRunner egy ingyenes, nyílt forráskódú tesztelő eszköztár és JUnit runner:
http://www.artima.com/suiterunner/index.html

4.JUnit GYIK kérdés a privát módszerek teszteléséről:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10

5. Privát módszerek tesztelése (ne csináld), Charles Miller weblog bejegyzése:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it

6. Andy Hunt és Dave Thomas a Pragmatic Unit Testing szerzői, amely a Pragmatic Store-ban érhető el.

7. JUnit Addons gyűjteménye segítő osztályok JUnit által létrehozott Vladimar R. Bossicard:
http://sourceforge.net/projects/junit-addons

PrivateAccessor a JUnit Addons osztálya, amely megkönnyíti a magántagok tesztelését:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html

9.PrivilegedAccessor osztály, amellyel hozzáférhet a privát tagokhoz:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java

10. Tesztvezérelt fejlesztés példával, írta Kent Beck, leírja a teszt-első technikát:
http://www.amazon.com/exec/obidos/ASIN/0321146530/

11. Teszt fertőzött, Kent Beck és Erich Gamma bemutatta Junitot a világnak:
http://junit.sourceforge.net/doc/testinfected/testing.htm

Unit Testing Private Methods, egy blogbejegyzés a NUnit-ról Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx

a Java hozzáférési védelmének felforgatása az egység teszteléséhez, egy O ‘ Reilly OnJava.com szerző: Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html

a RunnerSuite osztály, amelyből a cikk kódrészletei készültek, itt teljes egészében megjelenik:
http://www.artima.com/suiterunner/privateExample.html

miért Refactored JUnit
http://www.artima.com/suiterunner/why.html

Artima SuiteRunner bemutató, építési Megfelelőség és Egységtesztek az Artima SuiteRunner segítségével:
http://www.artima.com/suiterunner/tutorial.html

az Artima SuiteRunner használatának megkezdése, hogyan kell futtatni a disztribúcióban szereplő egyszerű példát:
http://www.artima.com/suiterunner/start.html

runnning JUnit tesztek Artima SuiteRunner, hogyan kell használni Artima SuiteRunner, mint egy JUnit futó futtatni a meglévő JUnit test suites:
http://www.artima.com/suiterunner/junit.html

Artima SuiteRunner Kezdőlap:
http://www.artima.com/suiterunner/index.html

Artima SuiteRunner letöltési oldal (be kell jelentkeznie Artima.com a kiadás letöltése):
http://www.artima.com/suiterunner/download.jsp

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

beszélj vissza!

van véleménye? Az olvasók már közzétettek 26 megjegyzéseket erről a cikkről. Miért nem adod hozzá a tiédet?

A szerzőről

Bill Venners az Artima Software, Inc.elnöke. és főszerkesztője Artima.com. ő a szerzője az Inside The Java Virtual Machine című könyvnek, amely programozó-orientált felmérés a Java platform architektúrájáról és belsejéről. Népszerű rovatai a JavaWorld magazinban a Java internals, az object-oriented design és a Jini témákkal foglalkoztak. Bill megalakulása óta aktív a Jini közösségben. Ő vezette a Jini közösség ServiceUI projektjét, amely elkészítette a ServiceUI API-t. A ServiceUI lett a de facto szabványos módja annak, hogy a felhasználói felületeket a Jini szolgáltatásokhoz társítsák, és ez volt az első Jini közösségi szabvány, amelyet a Jini döntési folyamata hagyott jóvá. Bill a Jini közösség kezdeti műszaki felügyeleti Bizottságának (TOC) megválasztott tagjaként is szolgál, és ebben a szerepben segített meghatározni a közösség irányítási folyamatát. Jelenleg energiájának nagy részét az építésre fordítja Artima.com egyre hasznosabb forrás a fejlesztők számára.

You might also like

Vélemény, hozzászólás?

Az e-mail-címet nem tesszük közzé.