Approach 1: Don ’ t Test Private Methods

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

tässä artikkelissa verrataan neljää eri lähestymistapaa yksityisten menetelmien testaamiseen Java-luokissa.

Junitin ensimmäinen käyttökohde oli rakentaa vaatimustenmukaisuuden testipaketti ServiceUI API: lle . Conformance test kit-testisarjan tarkoituksena on varmistaa, että saman API: n vaihtoehtoiset toteutukset ovat yhteensopivia API: n spesifikaation kanssa. Koska API-spesifikaatio määrittelee vain API: n julkisen käyttöliittymän, ei API: n toteutusta, vaatimustenmukaisuustestissä harjoitellaan vain julkista rajapintaa. Toisin sanoen konformaatiotesti on ”musta laatikko” – testi. Se käsittelee testattavaa API: ta mustana laatikkona, jonka ulkoinen käyttöliittymä on nähtävissä, mutta jonka sisäinen toteutus ei. Java API: n vaatimustenmukaisuustesti tarvitsee siis käyttää vain testattavien pakettien ja luokkien julkisia jäseniä. Ei ole tarvetta käyttää pakettitasoisia, suojattuja tai yksityisiä jäseniä.

kun myöhemmin sovelsin Junitia varsinaisten yksikkötestien kirjoittamiseen, huomasin haluavani kirjoittaa white box-testit-testit, joissa käytetään tietoa testattavien pakettien ja luokkien sisäisestä toteutuksesta. Siinä missä halusin testata vain julkisia menetelmiä vaatimustenmukaisuustesteissäni, halusin kirjoittaa yksikkötestejä pakettien saatavuuteen ja toisinaan myös yksityisiä menetelmiä sekä julkisia menetelmiä.

Daniel Steinberg näytti minulle yhteisen JUnit-tekniikan, jossa käytettiin rinnakkaisia lähdekoodipuita, minkä ansiosta pystyin sijoittamaan testiluokat samaan pakettiin kuin testattavat luokat, mutta pitämään ne eri hakemistossa. Näin testi-ja tuotantokoodi erotettiin selkeästi toisistaan. Sijoittamalla molemmat lähdepuut koeputkeen testiluokkani pääsivät käsiksi paketitason menetelmiin ja testattavan paketin luokkiin. Tämä jätti kuitenkin minulle ongelman kokeilla yksityisiä menetelmiä.

kun kysyin Danielilta yksityisten menetelmien testaamisesta, hän ehdotti lempeästi, että kokeilisin yksityisiä menetelmiä epäsuorasti kokeilemalla pakettien pääsyä ja julkisia menetelmiä, jotka kutsuvat yksityisiä. Tämä vastaus ei aivan tyydyttänyt minua, koska toisinaan todella tunsin tarvetta kokeilla suoraan yksityistä menetelmää. Minun alkuperäinen ratkaisu oli vain tehdä tällaisia yksityisiä menetelmiä package access, joka antoi minulle mahdollisuuden testata niitä suoraan JUnit testiluokista samassa paketissa rinnakkaisessa lähdepuussa. Tämä toimi hyvin, mutta sai oloni jotenkin likaiseksi. Vaikka yleensä huomasin, että ajattelu siitä, miten suunnitella rajapintoja, jotta ne voitaisiin helposti yksikkö testattu auttoi minua suunnittelemaan parempia rajapintoja, tässä tapauksessa tunsin olin tehdä suunnittelu hieman huonompi, jotta se testattavissa.

kun päädyin myöhemmin osallistumaan Frank Sommersin, Matt Gerransin ja minun Artima Suiterunneriksi julkaiseman teoksen luomiseen, vannoin tekeväni yksityisten menetelmien testaamisen helpommaksi Suiterunnerissa kuin Junitissa. Mutta tutkittuani erilaisia lähestymistapoja yksityisten menetelmien testaamiseen, päätin olla tekemättä mitään erityistä Suiterunnerissa tukemaan yksityisten menetelmien testaamista. Käytitpä siis Junitia tai Suiterunneria, sinulla on samat neljä perusmenetelmää yksityisten menetelmien testaamiseen:

  • älä testaa yksityisiä menetelmiä.
  • anna metodipaketille pääsy.
  • käytetään sisäkkäistä testiluokkaa.
  • käytä heijastusta.

tässä artikkelissa käsittelen näitä neljää lähestymistapaa yksityisten menetelmien testaamiseen Jaavalla. Aion tarkastella etuja ja haittoja kunkin ja yrittää valottaa, kun on järkevää käyttää kunkin lähestymistavan.

kuten johdannossa mainitsin, kuulin Daniel Steinbergiltä ensimmäisen neuvon tukahduttaa satunnaiset haluni kokeilla yksityisiä menetelmiä. Mutta Daniel ei ole ainoastaan tämän neuvon lähde, johon olen törmännyt. Se tuntuu olevan yleinen asenne Java-yhteisössä. Esimerkiksi JUnit FAQ toteaa:

yksityisten menetelmien testaaminen voi olla merkki siitä, että kyseiset menetelmät pitäisi siirtää toiseen luokkaan uudelleenkäytettävyyden edistämiseksi.

Charles Miller esitti samanlaisen näkökannan blogissaan :

jos sinulla on perusteellinen sarja testejä luokan altistetulle (ei-yksityiselle) käyttöliittymälle, näiden testien pitäisi luonteensa vuoksi varmistaa, että myös luokan yksityinen menetelmä toimii. Jos näin ei ole, tai jos käytössä on niin monimutkainen yksityinen menetelmä, että sitä pitää testata pois julkisten soittajien asiayhteydestä, pitäisin sitä koodinhajuna.

ja Dave Thomas ja Andy Hunt kirjoittavat kirjassaan Pragmatic Unit Testing:

ylipäätään mitään kapselointia ei kannata rikkoa testauksen vuoksi (tai kuten äiti tapasi sanoa: ”Älä paljasta privaattejasi!”). Useimmiten luokkaa pitäisi pystyä testaamaan sen julkisia menetelmiä käyttämällä. Jos on olemassa merkittäviä toimintoja, jotka on piilotettu yksityisen tai suojatun pääsyn taakse, se voi olla varoitusmerkki siitä, että siellä on toinen luokka siellä kamppailee päästä ulos.

uskon kaikkiin näihin neuvoihin. Useimmiten yksityisiä menetelmiä voidaan tehokkaimmin testata lähestymistavalla 1, välillisesti testaamalla niitä kutsuvia pakettitasoisia, suojattuja ja julkisia menetelmiä. Mutta väistämättä, jotkut ihmiset joissakin tilanteissa tuntuu, että suoraan testaus yksityisen menetelmän on oikea asia tehdä.

minulla on tapana luoda monia yksityisiä hyötytapoja. Nämä hyödyllisyysmenetelmät eivät usein tee mitään esimerkiksi tiedot, ne vain toimivat passed parametrit ja palauttaa tuloksen. Luon tällaisia menetelmiä, jotta soittotapa olisi helpompi ymmärtää. Se on tapa hallita monimutkaisuus täytäntöönpanoa luokan. Nyt, jos otan yksityisen menetelmän pois menetelmästä, joka jo toimii ja jolla on hyvä yksikkötestin kattavuus, niin nuo olemassa olevat yksikkötestit todennäköisesti riittävät. Minun ei tarvitse kirjoittaa lisää yksikkötestejä vain yksityistä menetelmää varten. Mutta jos haluan kirjoittaa yksityisen menetelmän ennen sen kutsumenetelmää, ja haluan kirjoittaa yksikkötestit ennen yksityisen menetelmän kirjoittamista, olen palannut siihen, että haluan suoraan testata yksityistä menetelmää. Kun kyseessä on yksityinen hyödyllisyys menetelmiä, en tunne tarvetta suoraan testata menetelmiä on, kuten JUnit FAQ asian ilmaisi, ” osoitus siitä, että nämä menetelmät olisi siirrettävä toiseen luokkaan edistää uudelleenkäytettävyyttä.”Näitä menetelmiä tarvitaan oikeastaan vain siinä luokassa, jossa he asuvat, ja itse asiassa niitä kutsutaan usein vain yhdellä muulla menetelmällä.

toinen syy, miksi joskus tunnen tarvetta testata yksityisiä menetelmiä suoraan, on se, että minulla on taipumus ajatella, että yksikkötestaus auttaa minua saavuttamaan vankan järjestelmän rakentamalla sen kestävistä osista. Jokainen osa on ”yksikkö”, jolle voin kirjoittaa ” yksikkötestejä.”Yksikkötestit auttavat minua varmistamaan, että jokainen yksikkö toimii oikein, mikä puolestaan auttaa minua rakentamaan järjestelmän, joka toimii kokonaisuutena oikein. Ensisijainen yksikkö mielestäni kannalta, kun ohjelmointi Java on luokka. Rakennan järjestelmiä luokista, ja yksikkötestit antavat minulle luottamusta siihen, että luokkani ovat kestäviä. Mutta jossain määrin olen myös samaa mieltä yksityisiä menetelmiä, joista koostan paketti-pääsy, suojattu, ja julkisia menetelmiä. Nämä yksityiset menetelmät ovat yksiköitä, joita voidaan testata yksilöllisesti. Tällaiset yksikkötestit antavat minulle luottamusta siihen, että yksityiset menetelmät toimivat oikein, mikä auttaa minua rakentamaan pakettien käyttöoikeuksia, suojattuja ja julkisia menetelmiä, jotka ovat kestäviä.

kuten johdannossa mainitsin, methods package Accessin antaminen oli ensimmäinen lähestymistapani yksityisten menetelmien testaamiseen Junitin kanssa. Tämä lähestymistapa itse asiassa toimii hyvin, mutta se tulee hieman kustannuksia. Kun näen private access specifierin menetelmässä, se kertoo minulle jotain, mitä haluan tietää—että tämä on osa luokan toteutusta. Tiedän, että voin sivuuttaa menetelmän, jos yritän vain käyttää paketin toisen luokan luokkaa. Voisin selvittää tämän pakettien käyttömenetelmästä katsomalla tarkemmin menetelmän nimeä, dokumentaatiota ja koodia, mutta sana private viestii tätä paljon tehokkaammin. Lisäksi suurin ongelma, joka minulla on tässä lähestymistavassa, on filosofinen. Vaikka en välitä ”breaking kapselointi vuoksi testaus,” kuten Dave ja Andy sanoisivat, en vain tunne hyvää rikkoa kapselointi tavalla, joka muuttaa paketti-tason API. Toisin sanoen, vaikka olen varsin innostunut testaamaan luokkien ei-julkisia menetelmiä, eli luomaan ”white-box”-yksikkötestejä, mieluummin testattavien luokkien API: a, mukaan lukien pakettitason API: a, ei muuteta näiden testien helpottamiseksi.

lähestymistapa 3: käytetään sisäkkäistä Testiluokkaa

kolmas tapa testata yksityisiä menetelmiä on sijoittaa staattinen testiluokka testattavan tuotantoluokan sisälle. Koska sisäkkäisellä luokalla on pääsy suljetun luokkansa yksityisiin jäseniin, se voi vedota suoraan yksityisiin menetelmiin. Staattinen Luokka itsessään voi olla pakettiliittymä, jolloin se voidaan ladata osana white box-testiä.

tämän lähestymistavan huonona puolena on se, että jos et halua sisäkkäisen testiluokan olevan käytettävissä deployment JAR-tiedostossasi, joudut tekemään hieman ylimääräistä työtä sen purkamiseksi. Myös, jotkut ihmiset eivät ehkä pidä siitä, että testikoodi sekoitetaan samaan tiedostoon kuin tuotantokoodi, vaikka toiset saattavat mieluummin tätä lähestymistapaa.

lähestymistapa 4: Käytä pohdintaa

neljättä lähestymistapaa yksityisten menetelmien testaamiseen ehdotti minulle Vladimir R. Bossicard, joka kirjoitti JUnit Addons . Eräänä päivänä lounaalla Vladimir valisti minulle, että java.lang.reflect API sisälsi menetelmiä, joiden avulla asiakaskoodi pystyi kiertämään Java-virtuaalikoneen pääsysuojausmekanismin. Hän kertoi myös, että hänen JUnit Addons-projektiinsa kuului Luokka, junitx.util.PrivateAccessor, joka avusti reflection API: n käytössä juuri tähän tarkoitukseen: yksikkötestien kirjoittamiseen, jotka manipuloivat testattavien luokkien yksityisiä jäseniä. ”JUnit FAQ” viittaa Charlie Hubbardin ja Prashant Dhotken kirjoittamaan samantyyppiseen luokkaan , jonka nimi on PrivilegedAccessor.

yksi etu peilausmenetelmän käyttämisessä yksityisten menetelmien testaamisessa on se, että se tarjoaa testikoodin ja tuotantokoodin puhtaan erottelun. Testejä ei tarvitse sisäkkäistä testattavan luokan sisällä, kuten lähestymistavassa 3. Sen sijaan ne voidaan sijoittaa muiden luokan pakettitasoisia ja julkisia menetelmiä käyttävien testien rinnalle. Lisäksi sinun ei tarvitse muuttaa testattavan luokan API: a. Toisin kuin lähestymistapa 2, Yksityiset menetelmät voivat pysyä yksityisinä. Toisin kuin lähestymistapa 3, Sinun ei tarvitse lisätä mitään ylimääräistä sisäkkäistä luokkaa pakettiyhteystasolla. Tämän lähestymistavan suurin haitta on, että testikoodi on paljon monisanaisempi, koska se käyttää reflection API: a. Lisäksi refactoring IDE: t, kuten Eclipse ja IntelliJ, eivät yleensä ole yhtä taitavia muuttamaan menetelmien nimiä, kun niihin viitataan nimellä Strings, joka on siirretty reflection API: n menetelmiin. Jos siis vaihdat private-menetelmän nimeä refactoring IDE: llä, saatat joutua tekemään joitakin muutoksia käsin testikoodiin.

esimerkki

antaakseni yhden esimerkin yksityisestä menetelmästä, joka mielestäni ansaitsee suoran yksikkötestauksen, poistin joitakin toimintoja luokanmainmenetelmästäorg.suiterunner.Runner.Runner.mainjäsentää komentoriviargumentteja ja suorittaa joukon testejä käynnistäen valinnaisesti graafisen käyttöliittymän. Poistamani menetelmäparseArgsIntoListstekee osan komentorivin argumenttien jäsentämisestä SuiteRunner-sovellukseen. Nyt testatakseni julkista menetelmää, joka kutsuu tätä yksityiseksi menetelmäksi, minun pitäisi testatamain. Tärkein, tietenkin, on koko sovellus, mikä tekee menetelmän melko vaikea testata. Itse asiassa minulla ei ole olemassa olevaa testiämain.

tässä vaiheessa voi ihmetellä, että jos kirjoitan ensin testejä testivetoisen kehityksen tyyliin, miten olen koskaan päätynyt kirjoittamaan jäsennyskoodia, jossa ei ollut yksikkötestejä? Suurin syy on se, että testitartuntani on tullut vaiheittain. En todella kiinni Yksikkö Testi flunssa kauan ennen kuin olin kuullut JUnit tai lukea testi tartunnan . Takaisin, kun olin rakentamassa Windows-sovelluksia C++, esimerkiksi, haluaisin kirjoittaa vähän koodia testata äskettäin toteutettu menetelmä, sitten suorittaa, että koodi ja katsella sen suorittaa astumalla testattavan menetelmän läpi debuggeri. Tällainen yksikkötestaus auttoi minua saavuttamaan kestävyyden, mutta itse testit eivät tarkistaneet oikeaa käyttäytymistä. Tarkistin itse oikean käyttäytymisen tarkkailemalla debuggerin kautta. Testejä ei automatisoitu, joten en tallentanut niitä, jotta ne voitaisiin ajaa myöhemmin uudelleen. Kun luin testi tartunnan, näin välittömästi arvon automatisointi testit ja pitää ne noin eräänlainen regressio testi jälkeen refactoring, mutta pitkään se ei ollut järkeä minulle kirjoittaa testejä ensin. Halusin kirjoittaa testit sen jälkeen, kun olen ottanut toiminnallisuuden käyttöön, koska silloin olin ajanut testit debuggerilla. Toinen syy, miksi en kirjoittanut testejä ensin Kehittäessäni suurta osaa Suiterunnerista, on se, että halusin kirjoittaa suiterunnerin testit itse Suiterunnerin kanssa, pyrkiessäni syömään omaa koiranruokaani. Ennen kuin Suiterunnerin basic API asettui, minulla ei ollut testaustyökalupakkia, jota halusin käyttää testien kirjoittamiseen.

sen jälkeen testivirus on kuitenkin ottanut vahvemman otteen minusta, ja nyt mieluummin kirjoitan yksikkötestejä ensin suurimman osan ajasta. Mieluummin kirjoittaa testejä ensin ei niinkään, koska huomaan päätyväni puhtaampiin malleihin, mitä yleensä mainostetaan testivetoisen kehityksen suurimpana hyötynä. Mieluummin kirjoitan testit ensin, koska huomaan, että usein jos sukellan koodiin paineen alla, tarkoituksenani kirjoittaa testi myöhemmin, testiä ei oikeastaan koskaan kirjoiteta. Suiterunnerilla itsellään on tässä vaiheessa hyvin vähän yksikkötestejä juuri tästä syystä. Tässä parseArgsIntoLists menetelmä:

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

suiterunnerin komentorivillä on kolmenlaisia tietoja, joita SuiteRunner käyttää testien suorittamiseen: runpath, reporters ja suites. parseArgsIntoLists – menetelmässä käydään läpi vain Strings: n joukkona hyväksytyt argumentit ja sijoitetaan jokainen argumentti johonkin luetteloon, runpathList, reportersList ja suitesList.

ennen kuin kirjoitan testin tälle yksityiselle menetelmälle, kysyisin, edustaako haluni kirjoittaa tämä yksikkötesti koodinhajua, kuten Charles Miller asian kirjoitti blogissaan? Osoittaako se, että parseArgsIntoLists pitäisi siirtää toiseen luokkaan uudelleenkäytettävyyden edistämiseksi, kuten JUnit FAQ ehdottaa? Sanoisivatko Dave ja Andy, että se on varoitusmerkki siitä, että tuolla on toinen luokka, joka yrittää päästä ulos? Ehkä. Voisin helposti luoda ArgumentsParser – luokan, jossa on vain muutama jäsennystyötä tekevä staattinen menetelmä. Sekä ArgumentsParser – Luokka että sen sisältämät menetelmät voisivat olla pakettiliittymiä, jolloin niitä olisi helppo testata. Mutta se ei tunnu oikealta minusta. Näitä menetelmiä kutsutaan vain Runner.main. Ne tuntuvat yksityisiltä keinoilta. Ainoa syy, miksi siirtäisin ne ArgumentsParser – luokkaan, on päästä testaamaan niitä. Itse asiassa käyttäisin lähestymistapaa numero 2: Tee Yksityiset menetelmät pakettiin pääsy.

sen sijaan päätin tässä esimerkissä ottaa lähestymistavan numero 4 ja käyttää pohdintaa. Katsoin sekä Vladimir Bossicardin junitx.utils.PrivateAccessor että Charlie Hubbardin ja Prashant Dhotken PrivilegedAccessor, mutta päätin, että kumpikaan ei auttanut minua aivan haluamallani tavalla. Ensinnäkin näillä luokilla molemmilla on kyky testata kenttiä varmistaakseen, että ne on asetettu oikein. Vielä en ole koskaan tuntenut tarvetta päästä suoraan yksityisille kentille yksikkötesteistä. Haluan vain kokeilla yksityisiä apuvälineitä. Suurin ongelma, joka minulla oli näiden kahden luokan kanssa, on kuitenkin se, miten he käsittelivät poikkeuksia, joita voidaan heittää, kun yritetään vedota yksityiseen menetelmään harkinnan kautta. Jokaisella luokalla on yksi tai useampia menetelmiä, joiden tehtävä on vedota menetelmään, jossa on pohdintaa. PrivilegedAccessor ’ s two invokeMethod methods passes any exception back to its caller, including three checked Exception declared in the throws clause: NoSuchMethodException, IllegalAccessException, and InvocationTargetException. Sen sijaan PrivateAccessor: n kahdella invoke menetelmällä saadaan kiinni InvocationTargetException, ja puretaan ja heitetään tavoitepoikkeus, kutsutulla menetelmällä heitetty todellinen poikkeus. Sen jälkeen se nappaa minkä tahansa muun poikkeuksen ja heittää NoSuchMethodException. En pitänyt siitä, että PrivilegedAccessor.invokeMethod soittajan tarvitsisi aina käsitellä kolme tarkistettua poikkeusta, koska ajattelin, että yleinen tapa käsitellä mikä tahansa poikkeus olisi antaa kokeen epäonnistua. Olin myös huolissani siitä, että PrivateAccessor.invoke oli heittämässä pois mahdollisesti hyödyllistä stack trace-tietoa poikkeusten käsittelypolitiikassaan. Mitä todella halusin oli menetelmä, joka yritti vedota yksityinen menetelmä pohdintaa, joka kääri kaikki heitetty poikkeus paitsi InvocationTargetException in valvomaton TestFailedException. Useimmiten tämä poikkeus aiheuttaisi testin epäonnistumisen. Testeissä, joissa odotettiin poikkeuksen heittämistä, InvocationTargetException: n sisältämä poikkeus voitiin purkaa ja testata oikeellisuutta.

siksi kirjoitin invokeStaticMethod. setAccessible(true) – kutsu mahdollistaa yksityisen menetelmän käytön luokan ulkopuolelta. Vastaava invokeStaticMethod toteutus Junitilla käytettäväksi heittäisi AssertionFailedError eikä TestFailedException. Tässä on koodi.:

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

seuraavaksi loin mukavuusmenetelmän, joka vetoaa tiettyyn yksityiseen menetelmään, jonka halusin testata:

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

vihdoinkin pystyin kirjoittamaan kokeita yksityistä metodia vastaan ilman liikaa sotkua, näin:

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

johtopäätös

lähestymistapa 1, yksityisten menetelmien epäsuora testaus testaamalla pakettitasoisia, suojattuja ja niitä kutsuvia julkisia menetelmiä, on usein paras lähestymistapa. Tapauksissa, joissa todella haluat testata yksityisiä menetelmiä suoraan, käyttämällä heijastus testata yksityisiä menetelmiä, vaikka melko hankala, ei tarjota puhtain erottaminen testikoodin tuotannon koodi, ja vähiten vaikutusta tuotannon koodi. Kuitenkin, jos et mielessä tehdä näitä erityisiä yksityisiä menetelmiä haluat testata paketti pääsy, voit käyttää lähestymistapaa 2. Tai jos et pane pahaksesi sisäkkäisen testiluokan sijoittamista tuotantoluokkasi sisälle testattavaksi, lähestymistapa 3 antaisi ainakin sinun pitää yksityiset menetelmät yksityisinä.

ei ole täydellistä vastausta. Mutta jos omaksut lähestymistavan 4, päädyt lopulta kouralliseen menetelmiä, kuten invokeStaticMethod, joita voit käyttää uudelleen. Kun kirjoittaa mukavuusmenetelmän, kuten invokeParseArgsIntoLists, yksityiselle menetelmälle, voi kirjoittaa testejä yksityistä menetelmää vastaan ilman suurempia vaikeuksia.

resurssit

1. ServiceUI API määrittelee standardin tavan liittää käyttöliittymiä Jini-palveluihin:
http://www.artima.com/jini/serviceui/index.html

2. Daniel Steinberg on tällä hetkellä päätoimittaja Java.NET:
http://www.java.net/

3. Artima SuiteRunner on ilmainen avoimen lähdekoodin testaus toolkit ja JUnit runner:
http://www.artima.com/suiterunner/index.html

4.JUnit FAQ kysymys yksityisten menetelmien testaamisesta:
http://junit.sourceforge.net/doc/faq/faq.htm#tests_10

5. Testing Private Methods (Don ’ t Do It), blogikirjoitus Charles Miller:
http://fishbowl.pastiche.org/2003/03/28/testing_private_methods_dont_do_it

6. Andy Hunt ja Dave Thomas ovat kirjoittaneet Pragmatic Unit Testing, joka on saatavilla Pragmatic Store.

7. JUnit Addons on Vladimar R. Bossicardin luoma kokoelma junitille tarkoitettuja apuluokkia:
http://sourceforge.net/projects/junit-addons

PrivateAccessor on JUnit Addons-luokka, joka helpottaa yksityisten jäsenten testaamista:
http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html

9.PrivilegedAccessor-luokka, jonka avulla voit käyttää yksityisiä jäseniä:
http://groups.yahoo.com/group/junit/files/src/PrivilegedAccessor.java

10. Test Driven Development by Example, by Kent Beck, describes the test-first technique:
http://www.amazon.com/exec/obidos/ASIN/0321146530/

11. Test Infected, Kent Beck ja Erich Gamma esittelivät Junitin maailmalle:
http://junit.sourceforge.net/doc/testinfected/testing.htm

Unit Testing Private Methods, a weblog post about nunit by Ted Graham:
http://weblogs.asp.net/tgraham/archive/2003/12/31/46984.aspx

Subverting Java ’s Access Protection for Unit Testing, an O’ Reilly OnJava.com artikkeli: Ross Burton:
http://www.onjava.com/pub/a/onjava/2003/11/12/reflection.html

Luokka RunnerSuite, josta tämän artikkelin koodinpätkät on otettu, näkyy kokonaisuudessaan tässä:
http://www.artima.com/suiterunner/privateExample.html

miksi Refactored JUnit
http://www.artima.com/suiterunner/why.html

Artima SuiteRunner Tutorial, Building Conformance ja Unit Tests with Artima SuiteRunner:
http://www.artima.com/suiterunner/tutorial.html

Artima Suiterunnerin käytön aloittaminen, kuinka voit käyttää jakeluun sisältyvää yksinkertaista esimerkkiä:
http://www.artima.com/suiterunner/start.html

Running JUnit testaa Artima Suiterunnerilla, kuinka käyttää Artima Suiterunneria JUnit runnerina JUnit test suites-testipakettien ajamiseen:
http://www.artima.com/suiterunner/junit.html

Artima Suiterunnerin Kotisivu:
http://www.artima.com/suiterunner/index.html

Artima SuiteRunner download page (sinun täytyy kirjautua Artima.com lataa julkaisu):
http://www.artima.com/suiterunner/download.jsp

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

vastatkaa!

onko mielipidettä? Lukijat ovat lähettäneet artikkeliin jo 26 kommenttia. Mikset lisää omaasi?

tekijästä

Bill Venners on Artima Software, Inc: n toimitusjohtaja. ja päätoimittaja Artima.com. hän on kirjoittanut kirjan Inside The Java Virtual Machine, ohjelmoijapainotteinen tutkimus Java-alustan arkkitehtuurista ja sisäosista. Hänen suositut kolumninsa JavaWorld-lehdessä käsittelivät Javan sisäosia, object-oriented designia ja Jiniä. Bill on ollut aktiivinen Jini-yhteisössä sen perustamisesta lähtien. Hän johti Jini-yhteisön ServiceUI-projektia, joka tuotti ServiceUI API: n. Serviceuista tuli de facto standardi tapa liittää käyttöliittymiä Jini-palveluihin, ja se oli ensimmäinen Jini-Yhteisöstandardi, joka hyväksyttiin Jini-päätöksentekoprosessin kautta. Bill toimii myös Jini-yhteisön alkuperäisen teknisen valvontakomitean (Toc) vaaleilla valittuna jäsenenä ja auttoi tässä tehtävässä määrittelemään yhteisön hallintoprosessin. Tällä hetkellä hän käyttää suurimman osan energiastaan rakentamiseen Artima.com yhä hyödyllisemmäksi resurssiksi kehittäjille.

You might also like

Vastaa

Sähköpostiosoitettasi ei julkaista.