Java Language
Enhetstestning
Sök…
Introduktion
Anmärkningar
Enhetstestramar
Det finns många ramar för enhetstestning inom Java. Det mest populära alternativet är JUnit. Det är dokumenterat under följande:
JUnit4 - Föreslagen tagg för JUnit4-funktioner; ännu inte genomfört .
Andra enhetstestramar finns och har dokumentation tillgänglig:
Enhetstestverktyg
Det finns flera andra verktyg som används för enhetstestning:
Mockito - Hånande ramverk; gör att objekt kan efterliknas. Användbart för att härma det förväntade beteendet hos en extern enhet inom en given enhets test, för att inte koppla den externa enhetens beteende till den givna enhetens tester.
JBehave - BDD- ramverk. Tillåter att test kopplas till användarbeteenden (möjliggör krav / scenariovalidering). Det finns inget dokumentmärke tillgängligt i skrivande stund; här är en extern länk .
Vad är enhetstestning?
Det här är lite grundare. Det läggs mest på det eftersom dokumentation tvingas ta ett exempel, även om den är avsedd som en stubartikel. Om du redan känner till grunderna för enhetstestning, känn dig fri att hoppa framåt till kommentarerna, där specifika ramverk nämns.
Enhetstestning säkerställer att en given modul fungerar som förväntat. I storskaliga applikationer är det att säkerställa korrekt utförande av moduler i vakuum en integrerad del av att säkerställa tillämpningens trovärdighet.
Tänk på följande (triviala) pseudoexempel:
public class Example {
public static void main (String args[]) {
new Example();
}
// Application-level test.
public Example() {
Consumer c = new Consumer();
System.out.println("VALUE = " + c.getVal());
}
// Your Module.
class Consumer {
private Capitalizer c;
public Consumer() {
c = new Capitalizer();
}
public String getVal() {
return c.getVal();
}
}
// Another team's module.
class Capitalizer {
private DataReader dr;
public Capitalizer() {
dr = new DataReader();
}
public String getVal() {
return dr.readVal().toUpperCase();
}
}
// Another team's module.
class DataReader {
public String readVal() {
// Refers to a file somewhere in your application deployment, or
// perhaps retrieved over a deployment-specific network.
File f;
String s = "data";
// ... Read data from f into s ...
return s;
}
}
}
Så detta exempel är trivialt; DataReader
hämtar data från en fil, skickar den till Capitalizer
, som konverterar alla tecken till stora bokstäver, som sedan skickas till Consumer
. Men DataReader
är starkt kopplad till vår applikationsmiljö, så vi skjuter upp testning av denna kedja tills vi är redo att distribuera en testrelease.
Antag nu, någonstans längs vägen i en utgåva, av okända skäl, getVal()
metoden getVal()
i Capitalizer
från att returnera en toUpperCase()
-sträng till en toLowerCase()
-sträng:
// Another team's module.
class Capitalizer {
...
public String getVal() {
return dr.readVal().toLowerCase();
}
}
Det är uppenbart att detta bryter förväntat beteende. Men på grund av de svåra processer som är involverade i utförandet av DataReader
kommer vi inte att märka detta förrän vår nästa testinstallation. Så dagar / veckor / månader går med detta fel som sitter i vårt system, och då ser produktchefen detta och vänder sig direkt till dig, teamledaren som är kopplad till Consumer
. "Varför händer detta? Vad har ni förändrat?" Uppenbarligen är du oklart. Du har ingen aning om vad som händer. Du ändrade inte någon kod som borde beröra detta; varför bryts det plötsligt?
Så småningom, efter diskussion mellan team och samarbete, spåras problemet och problemet löses. Men det väcker frågan; hur kunde detta ha förhindrats?
Det finns två uppenbara saker:
Test måste automatiseras
Vårt beroende av manuell testning låter detta fel obemärkt alltför länge. Vi behöver ett sätt att automatisera processen under vilken buggar introduceras direkt . Inte fem veckor från nu. Inte fem dagar från och med nu. Inte 5 minuter från och med nu. Just nu.
Du måste uppskatta att jag i detta exempel har uttryckt ett väldigt trivialt fel som introducerades och obemärkt. I en industriell applikation, där dussintals moduler ständigt uppdateras, kan dessa krypa in överallt. Du fixar något med en modul, bara för att inse att själva beteendet du "fixade" förlitades på på något sätt någon annanstans (antingen internt eller externt).
Utan noggrann validering kommer saker att krypa in i systemet. Det är möjligt att om det försummas tillräckligt långt kommer detta att resultera i så mycket extra arbete som försöker fixa förändringar (och sedan fixa dessa korrigeringar, etc.), att en produkt faktiskt kommer att öka i återstående arbete när ansträngningar läggs in i den. Du vill inte vara i den här situationen.
Tester måste finkornas
Det andra problemet som nämns i vårt exempel ovan är hur lång tid det tog att spåra felet. Produktchefen pingade dig när testarna märkte det, du undersökte och fann att Capitalizer
returnerade till synes dåliga data, du pingade Capitalizer
teamet med dina resultat, de undersökte, etc. etc. etc.
Samma poäng som jag nämnde ovan om mängden och svårigheten med detta triviala exempel här. Uppenbarligen kan alla som är rimligt välkända med Java hitta det införda problemet snabbt. Men det är ofta mycket, mycket svårare att spåra och kommunicera frågor. Kanske gav Capitalizer
teamet dig en JAR utan källa. Kanske är de belägna på andra sidan världen, och kommunikationstimmarna är mycket begränsade (kanske till e-postmeddelanden som skickas en gång dagligen). Det kan resultera i att buggar tar veckor eller längre tid att spåra (och, återigen, det kan finnas flera av dessa för en given utgåva).
För att mildra detta vill vi noggrant testa på en så fin nivå som möjligt (du vill också ha grovkorniga tester för att säkerställa att moduler samverkar korrekt, men det är inte vår kontaktpunkt här). Vi vill noggrant specificera hur all utåtriktad funktionalitet (minst) fungerar och testar för den funktionen.
Gå in i enhetstestning
Föreställ dig om vi hade ett test, särskilt att se till att getVal()
-metoden för Capitalizer
returnerade en aktiverad sträng för en given ingångssträng. Föreställ dig dessutom att testet kördes innan vi ens begick någon kod. Bugget som introducerats i systemet (det vill säga toUpperCase()
som ersätts med toLowerCase()
) orsakar inga problem eftersom felet aldrig skulle introduceras i systemet. Vi skulle fånga det i ett test, utvecklaren skulle (förhoppningsvis) inse sitt misstag och en alternativ lösning skulle nås hur man introducerar den avsedda effekten.
Det finns några utelämnanden här om hur man genomför dessa tester, men de behandlas i den ramspecifika dokumentationen (länkad i kommentarerna). Förhoppningsvis fungerar detta som ett exempel på varför enhetstestning är viktig.