Ricerca…
Osservazioni
Parametro | Contesto | Dettagli |
---|---|---|
@Prima della lezione | Statico | Eseguito quando la classe viene creata per la prima volta |
@Prima | Esempio | Eseguito prima di ogni test della classe |
@Test | Esempio | Dovrebbe essere dichiarato ogni metodo da testare |
@Dopo | Esempio | Eseguito dopo ogni test della classe |
@Dopo la lezione | Statico | Eseguito prima della distruzione della classe |
Esempio di formato della classe di test
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() {}
}
}
Test delle unità usando JUnit
Qui abbiamo un Counter
classe con metodi countNumbers()
e 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.*");
}
}
Per testare questa classe, possiamo usare il framework Junit. Aggiungi junit.jar
nel tuo percorso di classe del progetto. Quindi creare la classe Test case come di seguito:
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);
}
}
Nel tuo IDE puoi eseguire questa classe come "Junit testcase" e vedere l'output nella GUI. Nel prompt dei comandi è possibile compilare ed eseguire il test case come di seguito:
\> javac -cp ,;junit.jar CounterTest.java
\> java -cp .;junit.jar org.junit.runner.JUnitCore CounterTest
L'output di una prova di successo dovrebbe essere simile a:
JUnit version 4.9b2
..
Time: 0.019
OK (2 tests)
Nel caso di un fallimento del test, sembrerebbe più simile a:
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
infissi
Da Wikipedia :
Un dispositivo di prova è usato per testare costantemente un oggetto, un dispositivo o un pezzo di software.
Può anche migliorare la leggibilità dei test estraendo codice di inizializzazione / finalizzazione comune dai metodi di test stessi.
Nei casi in cui l'inizializzazione comune può essere eseguita una volta anziché prima di ogni test, ciò può anche ridurre il tempo necessario per eseguire i test.
L'esempio seguente è ideato per mostrare le opzioni principali fornite da JUnit. Assumi una classe Foo
che è costosa da inizializzare:
public class Foo {
public Foo() {
// expensive initialization
}
public void cleanUp() {
// cleans up resources
}
}
Un altro Bar
classe ha un riferimento a Foo
:
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
public void cleanUp() {
// cleans up resources
}
}
I test sottostanti prevedono un contesto iniziale di un elenco contenente una singola 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();
}
}
Nell'esempio il metodo annotato @BeforeClass
setupOnce()
viene utilizzato per creare l'oggetto Foo
, che è costoso da inizializzare. È importante che non venga modificato da nessuno dei test, altrimenti l'esito dell'esecuzione del test potrebbe dipendere dall'ordine di esecuzione dei singoli test. L'idea è che ogni test sia indipendente e testa una piccola caratteristica.
L' setup()
metodo @Before
annotated setup()
imposta il contesto di test. Il contesto può essere modificato durante l'esecuzione del test, motivo per cui deve essere inizializzato prima di ogni test. L'effetto equivalente potrebbe essere raggiunto includendo il codice contenuto in questo metodo all'inizio di ciascun metodo di prova.
Il metodo annotato @After
tearDown()
pulisce le risorse all'interno del contesto di test. Viene chiamato dopo ogni richiamo di test, e come tale viene spesso utilizzato per liberare risorse allocate in un metodo @Before
annotato.
Il metodo annotato @AfterClass
tearDownOnce()
ripulisce le risorse una volta eseguiti tutti i test. Tali metodi vengono in genere utilizzati per liberare risorse allocate durante l'inizializzazione o in un metodo annotato @BeforeClass
. Detto questo, è probabilmente meglio evitare le risorse esterne nei test unitari in modo che i test non dipendano da nulla al di fuori della classe di test.
Test delle unità usando le teorie
Da JavaDoc
The Theories runner consente di testare una determinata funzionalità rispetto a un sottoinsieme di un insieme infinito di punti dati.
Teorie correnti
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
}
}
I metodi annotati con @Theory
verranno letti come teorie da Teorie runner.
@DataPoint annotation
@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
}
}
Ogni campo annotato con @DataPoint
verrà utilizzato come parametro di metodo di un determinato tipo nelle teorie. Nell'esempio sopra theoryMethod
verrà eseguito due volte con i seguenti parametri: ["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
}
}
Ogni elemento dell'array annotato con @DataPoints
annotazione @DataPoints
verrà utilizzato come parametro di metodo di un determinato tipo nelle teorie. Nell'esempio sopra theoryMethod
verrà eseguito quattro volte con i seguenti parametri: ["str1", 1], ["str2", 1], ["str1", 2], ["str2", 2]
Valutazione della prestazione
Se è necessario verificare se il metodo di test impiega troppo tempo per essere eseguito, è possibile farlo menzionando il tempo di esecuzione previsto utilizzando la proprietà timeout dell'annotazione @Test. Se l'esecuzione del test richiede più tempo di quel numero di millisecondi, si verifica un errore nel metodo di prova.
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());
}
}
Nella maggior parte dei casi senza il testString
riscaldamento JVM, testString
fallirà. Ma testStringBuffer
e testStringBuilder
dovrebbero superare questo test con successo.