Jak napisać testy automatyczne w Javie
W artykule tym zajmiemy się pisaniem skryptów dla automatyzacji testów w Javie. Technika ta polega na wykorzystaniu odpowiednich narzędzi do wykonania testów na każdym commicie i po aktualizacji związanej z jakimś błędem.
Automatyzacja testów pomaga w zwiększeniu wydajności i dokładności Twojego systemu. Poza tym zrzuca to z Twoim barków większość żmudnej pracy manualnej, którą musisz wykonać, aby debugować lub testować swoją aplikację.
Tutorial ten skupia się na JUnit5, dojrzałym frameworku do testów, który składa się z kilku modułów pochodzących z 3 różnych podprojektów:
- Platforma JUnit: fundament do uruchamiania frameworków testowych na JVM. Zapewnia obsługę dla większości popularnych IDE (IntelliJ IDEA, Eclipse, NetBeans, and Visual Studio Code) i narzędzi do budowania (Gradle, Maven, and Ant).
- JUnit Jupiter: nowy model programowania dla testów automatycznych w JUnit 5
- JUnit Vintage: kompatybilny wstecznie silnik testowy, który obsługuje testy JUnit3 oraz JUnit 4.
Przejdźmy do następnej sekcji — zaczniemy pisać testy w Javie.
Pisanie skryptów testowych
Upewnij się, że Twoja lokalna maszyna działa z JDK8 lub nowszymi wersjami. Stwórz nowy plik Javy i nazwij go SimpleTest.java
.
Importowanie
Po pierwsze musimy zaimportować kilka głównych modułów JUnit Jupiter do pliku Java. Najprostszy przypadek testowy wymaga następujących podstawowych modułów:
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
Adnotacja
Następnie musisz umieścić adnotację swojej funkcji testowej z Test
i wywołać wymaganą asercję, która znajduje się wewnątrz.
Na przykład:
@Test
void singleAssertion() {
assertEquals(2, 1 + 1);
}
Funkcje z adnotacją Test
zostaną wywołane, gdy uruchomisz swój test.
Asercje
Poza assertEquals
, mamy jeszcze wiele funkcji assert
, których możesz użyć w swoim teście. Są to:
assertNotEquals
assertNull
assertNotNull
assertTimeout
assertTrue
assertAll
Kompletna lista asercji znajduje się tutaj. Jedną z najbardziej użytecznych asercji jest assertAll
— pomaga to w grupowaniu ich wszystkich*.
@Test
void groupedAssertions() {
assertAll("names",
() -> assertEquals("Jane", "John"),
() -> assertEquals("Doe", "Doe")
);
}
Oto kompletny kod Twojego pierwszego testu:
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class MyFirstJUnitJupiterTests {
@Test
void runningTest() {
assertEquals(2, 1 + 1);
}
}
Wyświetlanie nazwy
Niestandardowe nazwy w raporcie testowym można wyświetlać za pomocą modułu DisplayName
. Na wejście możemy podać ciąg znaków zawierający znaki alfanumeryczne, znaki specjalne, a nawet emoji:
@Test
@DisplayName("Test function")
void singleAssertion() {
assertEquals(2, 1 + 1);
}
Init oraz teardown
Jeżeli chcemy wykonać pewne funkcje przed lub po testach, możemy użyć adnotacji:
BeforeAll
BeforeEach
AfterAll
AfterEach
Spójrz na poniższy przykład — ilustruje on wszystkie adnotacje:
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
Wyłącz/włącz test
Wyłączanie testu jest bardzo proste — wystarczy dodać adnotację Disable
do metody. Na przykład:
@Test
@Disabled("Disable this test")
void skippedTest() {
}
Dodatkowo można wyłączyć test w określonych warunkach. Powiedzmy, że chcesz wyłączyć test w na Windowsie. Powinieneś wykorzystać adnotację DisableOnOs
, która akceptuje enum OS na górze funkcji.
@Test
@DisabledOnOs(OS.WINDOWS)
void notOnWindows() {
}
Kompletną listę wszystkich warunków możesz zobaczyć tutaj. Pomijając tę kwestię możesz w prosty sposób włączyć test w oparciu o to, czy spełnia on niestandardowy warunek. Dodaj po prostu EnableIf na górze funkcji testowej i przekaż ją w argumencie string
, który jest reprezentacją nazwy Twojego niestandardowego warunku:
@Test
@EnabledIf("customCondition")
void enabled() {
}
boolean customCondition() {
return true;
}
Testy sparametryzowane
JUnit5 obsługuje również sparametryzowane testy dzięki adnotacji ParameterizedTest
. Następujący przykład ilustruje to, jak możesz tworzyć test sprawdzający, czy wartość wejściowa jest dodatnia:
@ParameterizedTest
@ValueSource(ints = { -2, -1, 1, 2 })
void if_it_is_positive(int num) {
assertTrue(num > 0);
}
W przeciwieństwie do adnotacji Test
trzeba tutaj dostarczyć odpowiednie ValueSource
, które reprezentuje wszystkie parametry wejściowe w funkcji testowej.
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.condition.DisabledOnOs;
import org.junit.jupiter.api.condition.EnabledIf;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class SimpleTest{
@BeforeAll
static void initAll() {
}
@BeforeEach
void init() {
}
@Test
@DisplayName("Test function")
void singleAssertion() {
assertEquals(2, 1 + 1);
assertTrue(1 < 2);
}
@Test
void groupedAssertions() {
assertAll("names",
() -> assertEquals("Jane", "John"),
() -> assertEquals("Doe", "Doe")
);
}
@AfterEach
void tearDown() {
}
@AfterAll
static void tearDownAll() {
}
@Test
@Disabled("Disable this test")
void skippedTest() {
}
@Test
@DisabledOnOs(OS.WINDOWS)
void notOnWindows() {
}
@Test
@EnabledIf("customCondition")
void enabled() {
}
boolean customCondition() {
return true;
}
@ParameterizedTest
@ValueSource(ints = { -2, -1, 1, 2 })
void if_it_is_positive(int num) {
assertTrue(num > 0);
}
}
Uruchamianie testu
Większość popularnych IDE została już zintegrowana z JUnit jako część tego systemu. Jeśli masz jakieś problemy z uruchomieniem testu, przyjrzyj się następującym projektom przykładowym:
- Gradle i Java: junit5-jupiter-starter-gradle
- Gradle i Kotlin: junit5-jupiter-starter-gradle-kotlin
- Gradle i Groovy: junit5-jupiter-starter-gradle-groovy
- Maven: junit5-jupiter-starter-maven
- Ant: junit5-jupiter-starter-ant.
Narzędzie do uruchamiania konsoli
W tym tutorialu wykorzystamy oddzielne narzędzie do uruchamiania konsoli. Użyjemy tutaj wersji 1.7.1 pliku jar, dzięki któremu uruchomimy testy. Pobierz po prostu odpowiedni plik jar na swoją lokalną maszynę.
Budowanie klasy Javy
Teraz czas by stworzyć nowy folder o nazwie 'out' tam, gdzie pracujemy. Uruchom następującą komendę w terminalu, by zbudować klasę z testami.
javac -d out -cp junit-platform-console-standalone-1.7.1.jar out/SimpleTest.java
Nowy plik klasy Javy o nazwie SimpleTest.class
zostanie utworzony w folderze out
. Gdyby gdzieś pojawił się błąd, to oznacza to, że Twój plik Javy zawiera błędy.
Uruchamianie testu
Uruchom następującą komendę, aby rozpocząć testowanie Twojej klasy Javy, gdy już z powodzeniem ukończysz jej budowanie:
java -jar junit-platform-console-standalone-1.7.1.jar --class-path out --scan-class-path
Komenda ta przeskanuje wszystkie klasy Javy wewnątrz folderu out
i uruchomi odpowiednie testy. Tak to powinno wyglądać w konsoli:
Obraz należy do autora
Podsumowanie
Podsumujmy to, czego się dzisiaj nauczyliśmy. Tutorial ten zaczął się szybkim wytłumaczeniem koncepcji testów automatycznych i podstaw stojących za JUnit 5.
Następnie wytłumaczyliśmy sobie, w jaki sposób pisać testy przy użyciu JUnit Jupiter. Dotyczyło to podstawowych asercji, ale nauczyliśmy się też o parametryzowaniu testów.
Później przyjrzeliśmy się budowaniu plików testowych klas Javy i uruchamianiu testów bezpośrednio przez oddzielne narzędzie do uruchamiania konsoli.
Dziękuję za uwagę!
*Pomimo tego tworzenie wielu asercji w pojedynczym teście to nie jest najlepsza praktyka
Oryginał tekstu w języku angielskim możesz przeczytać tutaj.