Testimisraamistikud
Sissejuhatus
Testimisraamistikud pakuvad tööriistu testide kirjutamiseks ja käivitamiseks. Tööriistade hulka kuuluvad annotatsioonid testmeetodite märgistamiseks, väitemeetodid tulemuste kontrollimiseks ning testikäivitaja, mis leiab ja käivitab testid automaatselt.
Ilma raamistikuta peaksid iga testi jaoks kirjutama oma main-meetodi, käsitsi jälgima, millised testid läbisid, ja tulemused ise välja printima.
Raamistikud tegelevad selle kõigega, et saaksid keskenduda testiloogika kirjutamisele.
Javas on kaheks enimlevinud testimisraamistikuks JUnit 5 ja TestNG.
Kuidas JUnit 5 IntelliJ-is seadistada, saab lugeda siit.
JUnit 5
JUnit on Java ökosüsteemis kõige laialdasemalt kasutatav testimisraamistik.
Näidistest
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class CalculatorTest {
@Test
void testAddition() {
int result = Calculator.add(2, 3);
assertEquals(5, result);
}
@Test
void testAdditionWithNegativeNumbers() {
int result = Calculator.add(-1, -3);
assertEquals(-4, result);
}
}
Peamised tähelepanekud:
@Testmärgistab meetodi kui testimeetodina. Testide käivitaja automaatselt otsib kõik sellega märgistatud meetodid üles ja käivitab need.assertEquals(expected, actual)kontrollib, kas tegelik tulemus on võrdne oodatud tulemusega.- JUnit 5 puhul ei pea testimeetodid
publicolema. Erinevalt JUnit 4-st võivad meetodid kapackage-privateolla. - Klass, kus sees testid on, ei pea laiendama ühtegi teist klassi.
Annotatsioonid
JUnit 5 pakub erinevaid annotatsioone koodi käivitamiseks enne ja pärast teste:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.AfterAll;
class OrderServiceTest {
private OrderService service;
@BeforeAll
static void setupOnce() {
// Runs once before all tests in this class
// Must be static
}
@BeforeEach
void setup() {
// Runs before each test method
service = new OrderService();
}
@AfterEach
void cleanup() {
// Runs after each test method
}
@AfterAll
static void teardownOnce() {
// Runs once after all tests in this class
// Must be static
}
@Test
void testPlaceOrder() {
// 'service' is a fresh instance here
}
}
@BeforeEach on nende seast enimkasutatud.
Sellega tagatakse, et iga test käivitatakse puhtalt lehelt ehk ükski test ei sega mõnda teist testi.
Väited (assertions)
Väidete jaoks pakub JUnit 5 org.junit.jupiter.api.Assertions klassi koos järgnevate meetoditega:
import static org.junit.jupiter.api.Assertions.*;
assertEquals(expected, actual); // equality
assertEquals(expected, actual, "message"); // with failure message
assertNotEquals(a, b); // inequality
assertTrue(condition); // boolean check
assertFalse(condition); // boolean check
assertNull(value); // null check
assertNotNull(value); // not null check
assertThrows(IllegalArgumentException.class, // exception check
() -> service.process(null));
Pange tähele, et siin on kasutatud import static teegi sisse toomiseks.
See võimaldab näiteks assertEquals(...)-i välja kutsuda otse, ilma et peaks klassi nime ette tooma (nt: Assertions.assertEquals(...)).
Staatilisi importe kasutatakse selleks, et testimiskoodis hoida väiteid loetavamana.
Testide välja lülitamine
JUnit 5 samuti võimaldab teste ajutiselt välja lülitada @Disabled annotatsiooniga:
@Test
@Disabled("Waiting for bug #123 to be fixed")
void testFeatureThatIsBroken() {
// This test will be skipped
}
See on kasulik, kui teadaolev viga põhjustab testi ajutise läbikukkumise või kui funktsionaalsust parajasti ümber tehakse.
Erinevalt testi välja kommenteerimisest või kustutamisest jätab @Disabled selle testi aruandesse nähtavaks olekuga "skipped".
TestNG
TestNG on alternatiiv JUnit 5-le, mis pakub sarnast funktsionaalsust. Selle eesmärk oli esialgselt ära katta puuduolevat funktsionaalsust JUnit 4-st, kuid alates JUnit 5 turule tulekuga on need puudujäägid enamasti likvideeritud.
Näidistest
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
public class CalculatorTest {
@Test
public void testAddition() {
int result = Calculator.add(2, 3);
assertEquals(result, 5);
}
}
TestNG ja JUnit assertEquals erinevus seisneb selles, mis järjekorras parameetreid sisse võetakse.
TestNG puhul on esimeseks parameetriks tegelik tulemus ning selle järgneb oodatav tulemus, JUnit puhul vastupidi.
Annotatsioonid
TestNG annotatsioonid on erinevalt nimetatud, kuid järgivad JUnit-i vastetega sama käitumist.
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
public class OrderServiceTest {
private OrderService service;
@BeforeClass
public void setupOnce() {
// Runs once before all tests - does not need to be static
}
@BeforeMethod
public void setup() {
// Runs before each test method
service = new OrderService();
}
@AfterMethod
public void cleanup() {
// Runs after each test method
}
@AfterClass
public void teardownOnce() {
// Runs once after all tests
}
@Test
public void testPlaceOrder() {
// 'service' is a fresh instance here
}
}
Erinevalt JUnit 5-st peavad kõik TestNG meetodid olema public.
Samuti ei pea @BeforeClass ja @AfterClass meetodid olema static.
JUnit 5 vs TestNG
| Omadus | JUnit 5 | TestNG |
|---|---|---|
@Test | org.junit.jupiter.api.Test | org.testng.annotations.Test |
| Enne igat testi | @BeforeEach | @BeforeMethod |
| Pärast igat testi | @AfterEach | @AfterMethod |
| Enne kõiki teste | @BeforeAll (static) | @BeforeClass |
| Pärast kõiki teste | @AfterAll (static) | @AfterClass |
| Testi välja lülitamine | @Disabled | @Test(enabled = false) |
| assertEquals järjekord | (expected, actual) | (actual, expected) |
| Nähtavus | Package-private sobib | Peab olema public |
| Erindite testimine | assertThrows(...) | @Test(expectedExceptions = ...) |
JUnit 5 on lahendanud enamiku ajaloolistest erinevustest TestNG-ga, seega tänapäeval sõltub valik peamiselt eelistustest ja projekti kontekstist, mitte selgest tehnilisest üleolekust.
AssertJ - Fluent Assertions
Lõpetuseks tutvume ühe teegiga, mis teeb väidete kirjutamise sujuvamaks. Nii JUnit5 kui ka TestNG sisaldavad juba väitemehhanismi, kuid praktikas kasutatakse ka teeki nimega AssertJ, mille eesmärk on muuta väited loetavamaks ja sujuvamaks. Näiteks:
import static org.assertj.core.api.Assertions.assertThat;
// Instead of:
assertEquals("Alice", person.getName());
// AssertJ style:
assertThat(person.getName()).isEqualTo("Alice");
Seda assertThat(actual).isEqualTo(expected) rida saab lugeda kui lausena: "assert that the person's name is equal to Alice".
Seda on võimalik kasutada mitme erineva andmetüübi peal:
// Strings
assertThat(name).startsWith("Al").endsWith("ce").hasSize(5);
// Collections
assertThat(list).hasSize(3).contains("a", "b").doesNotContain("x");
// Numbers
assertThat(age).isGreaterThan(17).isLessThan(100);
// Floating-point comparison with tolerance
assertThat(celsius).isCloseTo(37.0, within(0.01));
assertThat(result).isCloseTo(0.3, offset(0.0001));
// Exceptions
assertThatThrownBy(() -> service.process(null))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("cannot be null");
AssertJ toimib nii Junit 5 kui ka TestNG-ga. See asendab ainult väitemehhanismi, mitte testide käivitajat või elutsükli annotatsioone.