Sök…
Anmärkningar
Parameter | Sammanhang | detaljer |
---|---|---|
@Innan lektionen | Statisk | Körs när klassen skapades första gången |
@Innan | Exempel | Utförs före varje test i klassen |
@Testa | Exempel | Bör förklaras varje metod att testa |
@Efter | Exempel | Utförs efter varje test i klassen |
@Efter lektionen | Statisk | Utförd innan klassens förstörelse |
Exempel Testklassformat
public class TestFeatureA {
@BeforeClass
public static void setupClass() {}
@Before
public void setupTest() {}
@Test
public void testA() {}
@Test
public void testB() {}
@After
public void tearDownTest() {}
@AfterClass
public static void tearDownClass() {}
}
}
Enhetstestning med JUnit
Här har vi en Counter
med metoderna countNumbers()
och hasNumbers()
.
public class Counter {
/* To count the numbers in the input */
public static int countNumbers(String input) {
int count = 0;
for (char letter : input.toCharArray()) {
if (Character.isDigit(letter))
count++;
}
return count;
}
/* To check whether the input has number*/
public static boolean hasNumber(String input) {
return input.matches(".*\\d.*");
}
}
För att testa den här klassen kan vi använda Junit-ramverket. Lägg till junit.jar
i din projektklassväg. Skapa sedan testfallsklassen enligt nedan:
import org.junit.Assert; // imports from the junit.jar
import org.junit.Test;
public class CounterTest {
@Test // Test annotation makes this method as a test case
public void countNumbersTest() {
int expectedCount = 3;
int actualCount = Counter.countNumbers("Hi 123");
Assert.assertEquals(expectedCount, actualCount); //compares expected and actual value
}
@Test
public void hasNumberTest() {
boolean expectedValue = false;
boolean actualValue = Counter.hasNumber("Hi there!");
Assert.assertEquals(expectedValue, actualValue);
}
}
I din IDE kan du köra denna klass som "Junit testcase" och se utgången i GUI. I kommandotolken kan du kompilera och köra testfallet enligt nedan:
\> javac -cp ,;junit.jar CounterTest.java
\> java -cp .;junit.jar org.junit.runner.JUnitCore CounterTest
Produktionen från en framgångsrik testkörning ska se ut som:
JUnit version 4.9b2
..
Time: 0.019
OK (2 tests)
Vid testfel ser det mer ut som:
Time: 0.024
There was 1 failure:
1) CountNumbersTest(CounterTest)
java.lang.AssertionError: expected:<30> but was:<3>
... // truncated output
FAILURES!!!
Tests run: 2, Failures: 1
fixturer
Från Wikipedia :
En testfixtur är något som används för att konsekvent testa någon artikel, enhet eller programvara.
Det kan också förbättra läsbarheten för tester genom att extrahera vanlig initialisering / slutföringskod från själva testmetoderna.
Där vanliga initialiseringar kan utföras en gång i stället för före varje test, kan detta också minska den tid det tar att köra tester.
Exemplet nedan är avsedd för att visa de viktigaste alternativen som tillhandahålls av JUnit. Anta en Foo
klass som är dyr att initialisera:
public class Foo {
public Foo() {
// expensive initialization
}
public void cleanUp() {
// cleans up resources
}
}
En annan Bar
har en referens till Foo
:
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
public void cleanUp() {
// cleans up resources
}
}
Testerna nedan förväntar sig ett initialt sammanhang för en lista som innehåller en enda Bar
.
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.List;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class FixturesTest {
private static Foo referenceFoo;
private List<Bar> testContext;
@BeforeClass
public static void setupOnce() {
// Called once before any tests have run
referenceFoo = new Foo();
}
@Before
public void setup() {
// Called before each test is run
testContext = Arrays.asList(new Bar(referenceFoo));
}
@Test
public void testSingle() {
assertEquals("Wrong test context size", 1, testContext.size());
Bar baz = testContext.get(0);
assertEquals(referenceFoo, baz.getFoo());
}
@Test
public void testMultiple() {
testContext.add(new Bar(referenceFoo));
assertEquals("Wrong test context size", 2, testContext.size());
for (Bar baz : testContext) {
assertEquals(referenceFoo, baz.getFoo());
}
}
@After
public void tearDown() {
// Called after each test is run
for (Bar baz : testContext) {
baz.cleanUp();
}
}
@AfterClass
public void tearDownOnce() {
// Called once after all tests have run
referenceFoo.cleanUp();
}
}
I exemplet används @BeforeClass
antecknade metoden setupOnce()
för att skapa Foo
objektet, vilket är dyrt att initiera. Det är viktigt att det inte modifieras av någon av testerna, annars kan resultatet av testkörningen bero på ordningen för utförandet av de enskilda testerna. Tanken är att varje test är oberoende och testar en liten funktion.
@Before
annoterad @Before
setup()
ställer in testsammansättningen. Kontextet kan ändras under testutförandet, varför det måste initieras före varje test. Den ekvivalenta effekten kan uppnås genom att inkludera koden i denna metod i början av varje testmetod.
@After
antecknade metoden tearDown()
rensar upp resurser inom testsammanhanget. Det kallas efter varje testanrop, och används som sådan för att frigöra resurser tilldelade i en @Before
annoterad metod.
@AfterClass
antecknade metoden tearDownOnce()
rensar upp resurser när alla tester har körts. Sådana metoder används vanligtvis för att frigöra resurser tilldelade under initialisering eller i en @BeforeClass
annoterad metod. Som sagt är det förmodligen bäst att undvika externa resurser i enhetstester så att testerna inte beror på något utanför testklassen.
Enhetstest med teorier
Från JavaDoc
Teoriköraren tillåter att testa en viss funktionalitet mot en delmängd av en oändlig uppsättning datapunkter.
Kör teorier
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
@RunWith(Theories.class)
public class FixturesTest {
@Theory
public void theory(){
//...some asserts
}
}
Metoder som anmälts med @Theory
kommer att läsas som teorier av Theories löpare.
@DataPoint-kommentar
@RunWith(Theories.class)
public class FixturesTest {
@DataPoint
public static String dataPoint1 = "str1";
@DataPoint
public static String dataPoint2 = "str2";
@DataPoint
public static int intDataPoint = 2;
@Theory
public void theoryMethod(String dataPoint, int intData){
//...some asserts
}
}
Varje fält som är antecknat med @DataPoint
kommer att användas som en metodparameter av en viss typ i teorierna. I exemplet ovan kommer theoryMethod
att köras två gånger med följande parametrar: ["str1", 2] , ["str2", 2]
@DataPoints annotation @RunWith (Theories.class) public class FixturesTest {
@DataPoints
public static String[] dataPoints = new String[]{"str1", "str2"};
@DataPoints
public static int[] dataPoints = new int[]{1, 2};
@Theory
public void theoryMethod(String dataPoint, ){
//...some asserts
}
}
Varje element i matrisen som antecknas med @DataPoints
kommer att användas som en metodparameter av en given typ i teorierna. I exemplet ovan kommer theoryMethod
att köras fyra gånger med följande parametrar: ["str1", 1], ["str2", 1], ["str1", 2], ["str2", 2]
Prestandamätning
Om du behöver kontrollera om din testmetod tar för lång tid att utföra kan du göra det genom att nämna din förväntade körningstid med timeout-egenskapen för @Test-annotation. Om testutförandet tar längre tid än det antalet millisekunder gör det att en testmetod misslyckas.
public class StringConcatenationTest {
private static final int TIMES = 10_000;
// timeout in milliseconds
@Test(timeout = 20)
public void testString(){
String res = "";
for (int i = 0; i < TIMES; i++) {
res += i;
}
System.out.println(res.length());
}
@Test(timeout = 20)
public void testStringBuilder(){
StringBuilder res = new StringBuilder();
for (int i = 0; i < TIMES; i++) {
res.append(i);
}
System.out.println(res.length());
}
@Test(timeout = 20)
public void testStringBuffer(){
StringBuffer res = new StringBufferr();
for (int i = 0; i < TIMES; i++) {
res.append(i);
}
System.out.println(res.length());
}
}
I de flesta fall kommer inte JVM- testString
att misslyckas. Men testStringBuffer
och testStringBuilder
borde klara det här testet framgångsrikt.