Springen naar inhoud

Java unit tests en visibility


  • Log in om te kunnen reageren

#1

ZVdP

    ZVdP


  • >1k berichten
  • 2097 berichten
  • VIP

Geplaatst op 16 maart 2011 - 19:10

Ik wil unit tests schrijven met JUnit. Hiervoor maak ik voor het overzicht een aparte package met alle test classes.

De code die ik wil testen is een pakketje code dat ik geschreven heb, met de bedoeling dat andere gebruikers (mijn teamleden) deze in hun code kunnen importeren.
Hiervoor heb ik alle methodes die enkel intern in het programma gebruikt worden, op 'protected' gezet, zodat deze niet extern gezien worden. Dit doe ik om de interface wat overzichtelijker te maken voor externe gebruikers die dus enkel de voor hen benodigde klasses en methodes zien.

Hier zit nu ook meteen het probleem met de unit tests. Ik wil die protected methodes testen, maar dat gaat natuurlijk niet vanuit de aparte test package.

Ik ken wel een oplossing, namelijk door Java reflection de methode te extraheren uit de klasse, de access checking uit te schakelen voor deze methode, en dan deze uit te voeren.
Maar dat is nogal een omslachtige en naar mijn mening een niet al te propere manier.

Ik vermoed dat er geen andere methode is dan ofwel de methodes weer public te maken, of de test classes niet in een aparte package te zetten?
"Why must you speak when you have nothing to say?" -Hornblower
Conserve energy: Commute with a Hamiltonian

Dit forum kan gratis blijven vanwege banners als deze. Door te registeren zal de onderstaande banner overigens verdwijnen.

#2

peterbill

    peterbill


  • >25 berichten
  • 58 berichten
  • Ervaren gebruiker

Geplaatst op 16 maart 2011 - 19:34

Enige opties die er zijn heb je al opgenoemd inderdaad. Zit niet veel anders op naar mijn weten.

#3

Cycloon

    Cycloon


  • >1k berichten
  • 4810 berichten
  • VIP

Geplaatst op 16 maart 2011 - 19:43

Wat je kan doen is klassen gaan mocken in een andere package. Je leidt af van de oorspronkelijke klasse, je kan dan public methods schrijven in die afgeleide klasse die simpelweg doordelegeren naar de protected methods.

#4

ZVdP

    ZVdP


  • >1k berichten
  • 2097 berichten
  • VIP

Geplaatst op 16 maart 2011 - 19:44

Er is waarschijnlijk geen manier om die access checks uit te schakelen in de compiler?

Want het is iets wat wel degelijk bestaat at runtime.
myMethod is een protected of private methode in de klasse MyClass. En dan kan ik volgende code zonder fouten runnen:
Method method=MyClass.class.getDeclaredMethod("myMethod");
method.setAccessible(true);
method.invoke(null);

Maar er is geen 'method.setAccessible(true)' equivalent voor de compiler/editor?
"Why must you speak when you have nothing to say?" -Hornblower
Conserve energy: Commute with a Hamiltonian

#5

ZVdP

    ZVdP


  • >1k berichten
  • 2097 berichten
  • VIP

Geplaatst op 16 maart 2011 - 19:50

Wat je kan doen is klassen gaan mocken in een andere package. Je leidt af van de oorspronkelijke klasse, je kan dan public methods schrijven in die afgeleide klasse die simpelweg doordelegeren naar de protected methods.

Goed idee.
Ik maak dus in mijn test folder een package aan met nieuwe klasses die mijn bestaande klasses 'extenden'. En deze nieuwe klasses kunnen dan natuurlijk wel de protected methodes zien.

Het enige jammere is dat ik ook nog private classes heb, aangezien protected classes niet toegestaan zijn.
Maar ja, Daar zal ik maar mee moeten leven dan ;)
"Why must you speak when you have nothing to say?" -Hornblower
Conserve energy: Commute with a Hamiltonian

#6

peterbill

    peterbill


  • >25 berichten
  • 58 berichten
  • Ervaren gebruiker

Geplaatst op 16 maart 2011 - 19:51

Nope, alleen via runtime wat je net opnoemde voorzover ik weet.

#7

Cycloon

    Cycloon


  • >1k berichten
  • 4810 berichten
  • VIP

Geplaatst op 16 maart 2011 - 21:52

Goed idee.
Ik maak dus in mijn test folder een package aan met nieuwe klasses die mijn bestaande klasses 'extenden'. En deze nieuwe klasses kunnen dan natuurlijk wel de protected methodes zien.


Inderdaad. Deze techniek kan ook perfect werken in andere situaties (je kan er wel wat meer info over vinden op google).

Het enige jammere is dat ik ook nog private classes heb, aangezien protected classes niet toegestaan zijn.
Maar ja, Daar zal ik maar mee moeten leven dan ;)


Daar valt inderdaad erg weinig aan te doen voor zover ik weet.

#8

ZVdP

    ZVdP


  • >1k berichten
  • 2097 berichten
  • VIP

Geplaatst op 16 maart 2011 - 22:16

Ik heb die klasses maar weer public gemaakt, maar met alles erin protected.

Ik mis eigenlijk gewoon een access modifier op niveau van een jar.
Misschien voor een vogende versie van Java ;)
"Why must you speak when you have nothing to say?" -Hornblower
Conserve energy: Commute with a Hamiltonian

#9

317070

    317070


  • >5k berichten
  • 5567 berichten
  • Moderator

Geplaatst op 16 maart 2011 - 23:44

Maar er is geen 'method.setAccessible(true)' equivalent voor de compiler/editor?

Waarom zou dat niet netjes zijn? http://junit.sourcef...aq.htm#tests_10
De Extractie-methode is dus wel degelijk de juiste methode. Maar voor protected raden ze blijkbaar aan om het gewoon mee te plaatsen in uw package ... ;)
What it all comes down to, is that I haven't got it all figured out just yet
And I've got one hand in my pocket and the other one is giving the peace sign
-Alanis Morisette-

#10

HvW

    HvW


  • 0 - 25 berichten
  • 2 berichten
  • Gebruiker

Geplaatst op 22 maart 2011 - 11:20

Dezelfde package impliceert niet dezelfde source directory.

Dus je kan een directory layout als:
src/packagefoo/ClassFoo.java
test/packagefoo/TestClassFoo.java
opzetten, waarbij beide classes in packagefoo zitten, maar de tests wel zijn gescheiden van de 'normale' source.

#11

coats001

    coats001


  • >25 berichten
  • 61 berichten
  • Ervaren gebruiker

Geplaatst op 10 juni 2011 - 13:24

Als je goede en volledige unittest voor de public methods heb geschreven zal ook al de code in de private/protected methods worden geraakt oftewel díe code word ook ge(unit)test. Er is dus geen enkele reden om de private/protected code "apart" te testen.

#12

Cycloon

    Cycloon


  • >1k berichten
  • 4810 berichten
  • VIP

Geplaatst op 11 juni 2011 - 12:18

Als je goede en volledige unittest voor de public methods heb geschreven zal ook al de code in de private/protected methods worden geraakt oftewel díe code word ook ge(unit)test. Er is dus geen enkele reden om de private/protected code "apart" te testen.


Ook niet helemaal correct. Want dan zou het betekenen dat we unit test achterwege kunnen laten en voldoende hebben aan integratietests. Vaak is het heel moeilijk om het resultaat van bepaalde methods te testen als dat resultaat reeds werd bewerkt door een andere opvolgende methode. Het kan dan best zijn dat het resultaat "toevallig" correct is, maar dat de private method die data moet aanleveren wel wat fout deed. Je komt dan ook pas in een heel specifieke situaties in de problemen die je bij je testen niet kon bedenken.

#13

coats001

    coats001


  • >25 berichten
  • 61 berichten
  • Ervaren gebruiker

Geplaatst op 14 juni 2011 - 10:11

Ook niet helemaal correct. Want dan zou het betekenen dat we unit test achterwege kunnen laten en voldoende hebben aan integratietests.

Nee dat zou het niet betekenen want integratietesten raken ook methods die public zijn "dieper in de keten". Dus methods die een andere scenario "in de top" van de aanroep keten had kunnen zitten. Immers je class zit een package en elke public method kan door iedereen die je package gebruikt worden aangeroepen. Het is dus van belang dat je ALLE mogelijk scenario's (aanroep mogelijkheden) test.

Een private method kan niet anders dan via een public method worden aangeroepen. Uitgangspunt is namelijk dat je class de meest basale unit binnen Java is. De unittest is dus een test voor de werking van een class, en de werking van een class is via haar public methods gedefinieerd. ALLE logica die in private methods is gedefineerd is alleen bereikbaar via de public methods van de omvattende class. Als je dus slechts unittests schrijf voor je public methods is er geen enkele reden voor bezorgdheid dat logica vervat is de private methods kan zorgen voor "ongeteste verrassingen".

#14

HvW

    HvW


  • 0 - 25 berichten
  • 2 berichten
  • Gebruiker

Geplaatst op 15 juni 2011 - 00:22

Ik ben het er wel mee eens dat het meestal niet zo mooi is om private methodes te testen. Deze methodes behoren tot de implementatie details van de klasse en zullen dus meestal vaker veranderen dan de interface v/d klasse (waarbij dan dus iedere keer de test mee moet worden aangepast). Daarnaast is het voor hergebruik van testcases (zie onder) en het gebruik van testen ter illustratie van hoe een klasse werkt, vaak handiger om de public, protected en package protected functionaliteit van een klasse alleen te testen via de public/protected/package protected functies, dus niet impliciet ook via tests op de private functies.

Package protected of protected functies horen wel bij de interface van de klasse. Het lijkt me een goed idee om unit tests te maken voor een utility klasse die binnen 1 package gebruikt wordt (dus waarvan alle functies package protected zijn), zoals ook voorgesteld door TS.

Voor protected functies geldt daarnaast per definitie dat ze protected zijn omdat ze overschreven moeten kunnen worden door een subklasse. Voor zowel de super klasse als de subklasse gelden vaak dezelfde set van assertions voor de protected functie (Liskov substitution principle). Er kan dus een testcase gemaakt worden die deze assertions test voor de superklasse. Deze test case kun je (of iemand anders die een subklasse bouwt van je klasse) hergebruiken voor alle subklasses.

#15

Cycloon

    Cycloon


  • >1k berichten
  • 4810 berichten
  • VIP

Geplaatst op 15 juni 2011 - 18:29

Nee dat zou het niet betekenen want integratietesten raken ook methods die public zijn "dieper in de keten". Dus methods die een andere scenario "in de top" van de aanroep keten had kunnen zitten. Immers je class zit een package en elke public method kan door iedereen die je package gebruikt worden aangeroepen. Het is dus van belang dat je ALLE mogelijk scenario's (aanroep mogelijkheden) test.

Een private method kan niet anders dan via een public method worden aangeroepen. Uitgangspunt is namelijk dat je class de meest basale unit binnen Java is. De unittest is dus een test voor de werking van een class, en de werking van een class is via haar public methods gedefinieerd. ALLE logica die in private methods is gedefineerd is alleen bereikbaar via de public methods van de omvattende class. Als je dus slechts unittests schrijf voor je public methods is er geen enkele reden voor bezorgdheid dat logica vervat is de private methods kan zorgen voor "ongeteste verrassingen".



Ik kan je wel volgen en dat klopt in de meeste gevallen waar een klasse een kleine eenheid is. Dat is echter niet in alle gevallen zo. Een klasse kan soms wel eens een heel eenvoudige interface hebben, maar intern een complexe verwerking vereisen. In zo'n gevallen is het vaak lastig/onmogelijk om zinvolle testen te produceren die enkel werken via de interface. Vooral frameworkcode heeft hier wel eens last van.





0 gebruiker(s) lezen dit onderwerp

0 leden, 0 bezoekers, 0 anonieme gebruikers

Ook adverteren op onze website? Lees hier meer!

Gesponsorde vacatures

Vacatures