Java unit tests en visibility

Moderators: jkien, Xilvo

Gebruikersavatar
Berichten: 2.097

Java unit tests en visibility

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

Berichten: 58

Re: Java unit tests en visibility

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

Gebruikersavatar
Berichten: 4.810

Re: Java unit tests en visibility

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.

Gebruikersavatar
Berichten: 2.097

Re: Java unit tests en visibility

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:

Code: Selecteer alles

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

Gebruikersavatar
Berichten: 2.097

Re: Java unit tests en visibility

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

Berichten: 58

Re: Java unit tests en visibility

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

Gebruikersavatar
Berichten: 4.810

Re: Java unit tests en visibility

ZVdP schreef: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).
ZVdP schreef: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.

Gebruikersavatar
Berichten: 2.097

Re: Java unit tests en visibility

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

Gebruikersavatar
Berichten: 5.609

Re: Java unit tests en visibility

Maar er is geen 'method.setAccessible(true)' equivalent voor de compiler/editor?
Waarom zou dat niet netjes zijn? http://junit.sourceforge.net/doc/faq/faq.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-

Re: Java unit tests en visibility

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.

Berichten: 61

Re: Java unit tests en visibility

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.

Gebruikersavatar
Berichten: 4.810

Re: Java unit tests en visibility

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.

Berichten: 61

Re: Java unit tests en visibility

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".

Re: Java unit tests en visibility

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.

Gebruikersavatar
Berichten: 4.810

Re: Java unit tests en visibility

coats001 schreef: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.

Reageer