CodeGym /Java Blog /Willekeurig /Unittesten in Java met JUnit
John Squirrels
Niveau 41
San Francisco

Unittesten in Java met JUnit

Gepubliceerd in de groep Willekeurig

Wat is unit-testen in Java?

Voordat we beginnen met het leren van JUnit in Java, laten we een kort overzicht geven van wat unit testing is en waarom het zo populair is (als je dit al weet, ga dan verder met 'Hoe schrijf ik een JUnit-test in Java?'). Unit testing in Java maakt grootschalige softwareontwikkeling veel efficiënter en gemakkelijker. Het kan zowel individuen als teams helpen om talloze uren te besparen op foutopsporing en het samenwerkingsproces enorm te stroomlijnen. Unittesten in Java met JUnit - 1

https://junit.org/junit4/

Het essentiële idee van het testen van eenheden is dit: schrijf atomaire tests van individuele functies (eenheidstests genoemd) en voeg langzaam meer functies toe na het testen en zorg ervoor dat de vorige werken. Het is een uiterst eenvoudig maar krachtig idee. Als voorbeeld van hoe dit proces eruit zou kunnen zien, stel je voor dat je een virtuele wetenschappelijke rekenmachine bouwt. Naast de schijnbare rekenkundige operatoren ( +, -, x, %), zou deze rekenmachine geavanceerde functies hebben die andere subfuncties vereisen om erin te werken. Om exponenten te berekenen, moet je rekenmachine correct kunnen vermenigvuldigen. Dus een benadering voor het testen van eenheden voor het bouwen en testen van deze rekenmachine zou zijn:
  • Schrijf een optelfunctie. Test het zorgvuldig, verander het, herhaal totdat het werkt.
  • Doe hetzelfde voor functies voor aftrekken, vermenigvuldigen en delen.
  • Gebruik deze basisoperatoren om meer geavanceerde operatorfuncties zoals exponenten te schrijven en test die functies vervolgens ook.
Dit zorgt ervoor dat functies die voortbouwen op andere kleinere subfuncties niet alleen op zichzelf goed werken, maar ook geen defecte subfuncties bevatten. Als ik bijvoorbeeld de exponentfunctie aan het testen ben en er gaat iets mis, dan weet ik dat de bug waarschijnlijk niet in de vermenigvuldigingssubfunctie zit, omdat de vermenigvuldigingsfunctie al uitgebreid is getest. Dit elimineert enorm de totale hoeveelheid code die ik nodig heb om terug te gaan en te inspecteren om de bug te vinden. Hopelijk maakt dit triviale voorbeeld duidelijk hoe het denkproces rond Unit Testing is gestructureerd. Maar hoe werkt het testen van eenheden samen met de rest van het softwareontwikkelingsproces? Wat als u nog complexere functies heeft, die moeten kunnen samenwerken en communiceren? Het testen van eenheden is onvoldoende om ervoor te zorgen dat dergelijke complexe functies goed kunnen samenwerken. In feite is dit slechts de eerste stap van de vier niveaus van softwaretesten (ik gebruik hoofdletters omdat ik verwijs naar de industriestandaard of de meest gebruikelijke benadering voor het testen van software). De laatste drie stappen zijnIntegratietesten , systeemtesten en acceptatietesten. Deze bedoelen waarschijnlijk allemaal precies wat u denkt dat ze doen, maar laat me verduidelijken: Integratietesten is wat we zouden doen om ervoor te zorgen dat de hierboven genoemde 'complexe functies' op de juiste manier met elkaar omgaan. (bijv. ervoor zorgen dat de rekenmachine "3 + 7 * 4 - 2" aankan). Systeemtesten is het testen van het algehele ontwerp van een bepaald systeem; er zijn vaak meerdere systemen met complexe functies die samenwerken in een product, dus u groepeert deze in systemen en test ze afzonderlijk. (bijv. als je een grafische rekenmachine zou bouwen, zou je eerst het rekenkundige 'systeem' bouwen om met getallen om te gaan, testen totdat het werkt zoals bedoeld, en dan zou je het grafische 'systeem' bouwen en testen om het terugtrekken, zoals het zou voortbouwen op het rekenstelsel). Acceptatietesten zijn testen op gebruikersniveau; het is kijken of alle systemen synchroon kunnen werken om een ​​afgewerkt product te creëren dat klaar is om door gebruikers te worden geaccepteerd (bijvoorbeeld gebruikers die de rekenmachine testen). Softwareontwikkelaars kunnen deze laatste stap van het proces soms negeren, omdat bedrijven vaak andere werknemers afzonderlijk gebruikers(bèta)tests laten uitvoeren.

Hoe schrijf ik een JUnit-test in Java?

Nu u een beter idee heeft van de voordelen en beperkingen van het testen van eenheden, laten we eens wat code bekijken! We zullen een populair Java-testraamwerk gebruiken, JUnit genaamd (een andere populaire is TestNG, die je ook kunt gebruiken als je wilt. Ze lijken syntactisch erg op elkaar; TestNG is geïnspireerd door JUnit). U kunt JUnit hier downloaden en installeren . Voor deze voorbeeldcode gaan we verder met het voorbeeld 'wetenschappelijke rekenmachine' dat ik eerder noemde; het is vrij eenvoudig om je hoofd eromheen te wikkelen, en de testcode is supergemakkelijk. Het is gebruikelijk om aparte testklassen te schrijven voor elk van je klassen, dus dat gaan we doen. Laten we aannemen dat we op dit punt een Math.javabestand hebben met alle wiskundige functies erin (inclusief Math.add), en we schrijven eenMathTests.javabestand in hetzelfde pakket. Laten we nu importstatements en class body instellen: (MOGELIJKE JUnit INTERVIEW VRAAG: Mogelijk wordt u gevraagd waar u uw JUnit-test moet plaatsen en of u uw bronbestanden al dan niet moet importeren. Als u uw testklassen in hetzelfde pakket schrijft als uw hoofdklassen, dan heeft u geen importinstructies nodig voor uw bronbestanden in de testklasse. Zorg er anders voor dat u uw bronbestanden importeert!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
De eerste importopdracht geeft ons de @Testkoptekst. We schrijven ' @Test' direct boven elke testfunctiedefinitie, zodat JUnit weet dat dit een enkelvoudige unit-test is die afzonderlijk kan worden uitgevoerd. Later zal ik je laten zien hoe je specifieke unit-tests kunt uitvoeren met behulp van deze header. De tweede importopdracht bespaart ons wat typwerk. De primaire JUnit-functie die we gebruiken om onze functies te testen is to Assert.assertEquals(), die twee parameters (werkelijke waarde en verwachte waarde) nodig heeft en ervoor zorgt dat ze gelijk zijn. Met deze tweede importopdracht kunnen we gewoon ' ' typen assertEquals(...in plaats van elke keer te moeten specificeren van welk pakket het deel uitmaakt. Laten we nu een heel eenvoudige testcase schrijven om te verifiëren dat 2 + 2 inderdaad 4 is!

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
Laten we elk van de vijf regels van de testfunctie bekijken en wat ze doen: Regel 5: Deze @Testkop geeft aan dat de onderstaande functiedefinitie add_twoPlusTwo_returnsFour()inderdaad een testfunctie is die JUnit afzonderlijk kan uitvoeren. Regel 6: Dit is de functiehandtekening voor onze testcase. Testgevallen zijn altijd heel bijzonder; ze testen slechts één specifiek voorbeeld, zoals 2+2=4. Het is gebruikelijk om uw testcases een naam te geven in de vorm ' [function]_[params]_returns[expected]()', waarbij [function]de naam is van de functie die u aan het testen bent, [params]de specifieke voorbeeldparameters die u aan het testen bent en [expected]de verwachte retourwaarde van de functie. Testfuncties hebben bijna altijd het retourtype ' void' omdat het belangrijkste punt van de hele functie is om uit te voerenassertEquals, die naar de console wordt uitgevoerd, ongeacht of uw test is geslaagd; je hebt geen andere gegevens nodig om ergens terug te keren. Regel 7: We declareren een ' final' variabele van het retourtype van Math.add (int), en noemen deze volgens afspraak 'verwacht'. De waarde ervan is het antwoord dat we verwachten (4). Regel 8: We declareren een ' final' variabele van het retourtype van Math.add (int), en noemen deze volgens afspraak 'werkelijk'. De waarde is het resultaat van Math.add(2, 2). Lijn 9: De gouden lijn. Dit is de regel die de werkelijke en verwachte vergelijkingen vergelijkt en ons vertelt dat we alleen geslaagd zijn voor de test als ze gelijk zijn. De eerste doorgegeven parameter “2+2 is 4” is een beschrijving van de testfunctie.

Wat als mijn functie een uitzondering moet genereren?

Als uw specifieke testvoorbeeld een uitzondering zou opleveren in plaats van te beweren dat een werkelijke en verwachte waarde gelijk zijn, dan heeft JUnit een manier om dit in de @Testkoptekst te verduidelijken. Laten we eens kijken naar een voorbeeld hieronder. Ervan uitgaande dat we een functie hebben in Math.javagenaamd Math.divide, willen we ervoor zorgen dat invoer niet door 0 kan worden gedeeld. In plaats daarvan zou het aanroepen Math.divide(a, 0)van een 'a'-waarde een uitzondering moeten opleveren ( ArithmeticException.class). We specificeren dit als zodanig in de koptekst:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
U kunt meer dan één uitzondering hebben voor expectedExceptions, zorg er alleen voor dat u haakjes en komma's gebruikt om uw uitzonderingsklassen op te sommen, als zodanig:

expectedException = {FirstException.class, SecondException.class, … }

Hoe voer ik mijn JUnit-tests uit in Java?

JUnit toevoegen aan IntelliJ: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea U kunt uw project uitvoeren zoals u normaal de tests zou uitvoeren. Als u alle tests in een testklasse uitvoert, worden ze in alfabetische volgorde uitgevoerd. In JUnit 5 kunt u een prioriteit aan de tests toevoegen door een @Ordertag toe te voegen. Een voorbeeld:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
Hoewel alfabetisch en in de code a_test()vóór komt , zal hier eerder worden uitgevoerd , omdat 1 vóór 2 komt in volgorde. Dus dat is ongeveer alles voor de basis van JUnit. Laten we nu een paar veelvoorkomende JUnit-interviewvragen behandelen en gaandeweg wat meer leren over JUnit! b_test()b_test()a_test()

JUnit-sollicitatievragen (aanvullende informatie)

Hier heb ik de meest populaire JUnit-interviewvragen verzameld. Als u iets toe te voegen heeft, kunt u dit doen in de opmerkingen hieronder. Vraag: Welke methode kunt u in uw testmethode gebruiken om automatisch een test te laten mislukken? A: fail(“foutbeschrijving hier!”); Vraag: U test een hondenklasse; om een ​​Dog-object te testen, moet u het instantiëren voordat u er tests op kunt uitvoeren. Dus je schrijft een setUp() functie om de Dog te instantiëren. U wilt deze functie slechts één keer uitvoeren tijdens het testen. Wat moet u direct boven de functiehandtekening setUp() plaatsen, zodat JUnit weet dat setUp() moet worden uitgevoerd voordat de tests worden uitgevoerd? A: @BeforeClass (@BeforeAll in JUnit 5) V:Wat moet de functiehandtekening zijn van de hierboven beschreven functie setUp()? A: openbare statische leegte. Elke functie met @BeforeClass (@BeforeAll in JUnit 5) of @AfterClass (@AfterAll in JUnit 5) moet statisch zijn. Vraag: Je bent klaar met het testen van de Dog-klasse. U schrijft de functie void tearDown() die gegevens opschoont en informatie afdrukt naar de console na elke test. U wilt dat deze functie na elke afzonderlijke test wordt uitgevoerd. Wat moet u direct boven de handtekening van de functie tearDown() plaatsen, zodat JUnit weet dat het na het uitvoeren van elke test tearDown() moet uitvoeren? A: @After (@AfterEach in JUnit 5)
Opmerkingen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION