- Testowanie integracji bazy danych przy użyciu MariaDB w celu zastąpienia MySql
- Wdrożenie aplikacji wielojęzycznej
- Zapisywanie plików do aplikacji oraz danych o nich do bazy danych
Rodzaje testów
Co to jest test? Jak mówi Wiki: „ Test lub test to sposób badania podstawowych procesów systemu poprzez umieszczanie systemu w różnych sytuacjach i śledzenie obserwowalnych w nim zmian”. Inaczej mówiąc, jest to test poprawności działania naszego systemu w określonych sytuacjach. Zobaczmy, jakie są rodzaje testów:-
Testy jednostkowe to testy, których zadaniem jest przetestowanie każdego modułu systemu z osobna. Pożądane jest, aby były to minimalnie podzielne elementy systemu, na przykład moduły.
-
Testowanie systemu to test wysokiego poziomu, mający na celu przetestowanie działania większego fragmentu aplikacji lub systemu jako całości.
-
Testowanie regresyjne to testowanie, które służy do sprawdzenia, czy nowe funkcje lub poprawki błędów wpływają na istniejącą funkcjonalność aplikacji i czy pojawiają się ponownie stare błędy.
-
Testowanie funkcjonalne polega na sprawdzeniu zgodności części aplikacji z wymaganiami określonymi w specyfikacjach, historiach użytkownika itp.
Rodzaje testów funkcjonalnych:
- Test „białej skrzynki” na zgodność części aplikacji z wymaganiami ze znajomością wewnętrznego wdrożenia systemu;
- Test „czarnej skrzynki” na zgodność części aplikacji z wymaganiami bez wiedzy o wewnętrznej implementacji systemu.
- Testowanie wydajności to rodzaj testów pisanych w celu określenia prędkości, z jaką system lub jego część działa pod określonym obciążeniem.
- Testy obciążeniowe - testy mające na celu sprawdzenie stabilności systemu pod standardowymi obciążeniami i znalezienie maksymalnego możliwego szczytu, przy którym aplikacja działa poprawnie.
- Testy obciążeniowe to rodzaj testów mających na celu sprawdzenie funkcjonalności aplikacji pod niestandardowymi obciążeniami i określenie maksymalnego możliwego szczytu, przy którym system nie ulegnie awarii.
- Testy bezpieczeństwa - testy służące do sprawdzenia bezpieczeństwa systemu (przed atakami hakerów, wirusami, nieautoryzowanym dostępem do poufnych danych i innymi przyjemnościami życia).
- Testowanie lokalizacji to testowanie lokalizacji aplikacji.
- Testowanie użyteczności to rodzaj testów mających na celu sprawdzenie użyteczności, zrozumiałości, atrakcyjności i łatwości uczenia się dla użytkowników. Wszystko brzmi nieźle, ale jak to wygląda w praktyce? To proste: zastosowano piramidę testową Mike'a Cohna: Jest to uproszczona wersja piramidy: teraz jest ona podzielona na mniejsze części. Ale dzisiaj nie będziemy wypaczać i rozważać najprostszej opcji.
-
Jednostka - testy jednostkowe stosowane w różnych warstwach aplikacji, testujące najmniejszą podzielną logikę aplikacji: na przykład klasę, ale najczęściej metodę. Testy te zazwyczaj starają się jak najbardziej odizolować od logiki zewnętrznej, czyli stworzyć iluzję, że reszta aplikacji pracuje w trybie standardowym.
Takich testów powinno być zawsze dużo (więcej niż inne typy), ponieważ testują małe fragmenty i są bardzo lekkie, a przy tym nie zużywają dużo zasobów (przez zasoby mam na myśli pamięć RAM i czas).
-
Integracja - testowanie integracyjne. Sprawdza większe fragmenty systemu, czyli jest albo kombinacją kilku fragmentów logiki (kilku metod lub klas), albo poprawnością pracy z komponentem zewnętrznym. Zwykle jest mniej takich testów niż testów jednostkowych, ponieważ są one cięższe.
Jako przykład testów integracyjnych można rozważyć połączenie się z bazą danych i sprawdzenie poprawności wykonania metod z nią pracujących .
-
UI - testy sprawdzające działanie interfejsu użytkownika. Wpływają na logikę na wszystkich poziomach aplikacji, dlatego nazywane są również end-to-end. Z reguły jest ich znacznie mniej, ponieważ są najciężsi i muszą sprawdzać najbardziej potrzebne (używane) ścieżki.
Na powyższym rysunku widzimy stosunek pól różnych części trójkąta: liczba tych testów w rzeczywistej pracy zachowuje w przybliżeniu tę samą proporcję.
Dzisiaj przyjrzymy się bliżej najczęściej używanym testom - testom jednostkowym, gdyż każdy szanujący się programista Java powinien umieć z nich korzystać na podstawowym poziomie.
- materiały na temat pokrycia kodu w JavaRush i Habré ;
- podstawowa teoria testowania .
- Piszemy nasz test.
- Przeprowadzamy test, czy zdał, czy nie (widzimy, że wszystko jest czerwone - nie panikuj: tak powinno być).
- Dodajemy kod, który powinien spełnić ten test (uruchom test).
- Refaktoryzujemy kod.
- Określanie danych do testowania (urządzenia).
- Użycie testowanego kodu (wywołanie testowanej metody).
- Sprawdzanie wyników i porównywanie ich z oczekiwanymi.
assertEquals(Object expecteds, Object actuals)
— sprawdza, czy przesyłane obiekty są równe.assertTrue(boolean flag)
— sprawdza, czy przekazana wartość zwraca wartość true.assertFalse(boolean flag)
— sprawdza, czy przekazana wartość zwraca wartość false.assertNull(Object object)
– sprawdza, czy obiekt ma wartość null.assertSame(Object firstObject, Object secondObject)
— sprawdza, czy przekazane wartości odnoszą się do tego samego obiektu.assertThat(T t, Matcher<T> matcher)
— sprawdza, czy t spełnia warunek określony w dopasowaniu.
Kluczowe pojęcia testów jednostkowych
Pokrycie testów (Code Coverage) to jedna z głównych ocen jakości testowania aplikacji. Jest to procent kodu objęty testami (0-100%). W praktyce wiele osób goni za tym procentem, z czym się nie zgadzam, bo zaczynają dodawać testy tam, gdzie nie są potrzebne. Przykładowo nasza usługa posiada standardowe operacje CRUD (utwórz/pobierz/aktualizuj/usuń) bez dodatkowej logiki. Metody te są jedynie pośrednikami delegującymi pracę do warstwy współpracującej z repozytorium. W tej sytuacji nie mamy nic do testowania: być może, czy ta metoda wywołuje metodę z Tao, ale to nie jest poważne. Aby ocenić zasięg testów, zwykle stosuje się dodatkowe narzędzia: JaCoCo, Cobertura, Clover, Emma itp. Aby uzyskać bardziej szczegółowe badanie tego problemu, zachowaj kilka odpowiednich artykułów:Etapy testowania
Test składa się z trzech etapów:Środowiska testowe
Przejdźmy więc teraz do rzeczy. Istnieje kilka środowisk testowych (frameworków) dostępnych dla języka Java. Najpopularniejsze z nich to JUnit i TestNG. Do naszego przeglądu używamy: Test JUnit to metoda zawarta w klasie, która jest używana tylko do testowania. Klasa ma zazwyczaj taką samą nazwę jak klasa, którą testuje, z opcją +Test na końcu. Na przykład CarService → CarServiceTest. System kompilacji Mavena automatycznie uwzględnia takie klasy w obszarze testowym. W rzeczywistości ta klasa nazywa się klasą testową. Przejrzyjmy trochę podstawowe adnotacje: @Test - definicja tej metody jako metody testowej (w rzeczywistości metoda oznaczona tą adnotacją jest testem jednostkowym). @Before - zaznacza metodę, która będzie wykonywana przed każdym testem. Np. wypełnienie danych testowych klasy, odczyt danych wejściowych itp. @After - umieszczony nad metodą, która będzie wywoływana po każdym teście (czyszczenie danych, przywracanie wartości domyślnych). @BeforeClass - umieszczony nad metodą - analogicznie do @Before. Jednak ta metoda jest wywoływana tylko raz przed wszystkimi testami dla danej klasy i dlatego musi być statyczna. Służy do wykonywania bardziej wymagających operacji, takich jak podnoszenie testowej bazy danych. @AfterClass jest przeciwieństwem @BeforeClass: wykonywane raz dla danej klasy, ale wykonywane po wszystkich testach. Używany na przykład do czyszczenia trwałych zasobów lub odłączania się od bazy danych. @Ignore - zauważa, że poniższa metoda jest wyłączona i będzie ignorowana podczas ogólnego uruchamiania testów. Stosuje się go w różnych przypadkach, na przykład, jeśli zmieniono metodę podstawową i nie było czasu na ponowne wykonanie testu. W takich przypadkach wskazane jest również dodanie opisu - @Ignore("Jakiś opis"). @Test (oczekiwany = wyjątek.klasa) - używany w przypadku testów negatywnych. Są to testy, które sprawdzają, jak metoda zachowuje się w przypadku błędu, czyli oczekuje, że metoda zgłosi jakiś wyjątek. Taka metoda jest oznaczona adnotacją @Test, ale zawiera błąd do wyłapania. @Test(timeout=100) - sprawdza, czy metoda zostanie wykonana w czasie nie dłuższym niż 100 milisekund. @Mock - nad polem używana jest klasa, aby ustawić dany obiekt jako mock (nie jest to z biblioteki Junit, tylko z Mockito), a jeśli będzie taka potrzeba, ustawimy zachowanie makiety w konkretnej sytuacji bezpośrednio w metodzie badawczej. @RunWith(MockitoJUnitRunner.class) - metoda umieszczana jest nad klasą. Jest to przycisk umożliwiający uruchomienie w nim testów. Runnery mogą być różne: na przykład są to: MockitoJUnitRunner, JUnitPlatform, SpringRunner itp.). W JUnit 5 adnotacja @RunWith została zastąpiona mocniejszą adnotacją @ExtendWith. Przyjrzyjmy się niektórym metodom porównywania wyników:assertThat(firstObject).isEqualTo(secondObject)
Tutaj mówiłem o podstawowych metodach, ponieważ reszta to różne odmiany powyższych.
Praktyka testowania
Przyjrzyjmy się teraz powyższemu materiałowi na konkretnym przykładzie. Przetestujemy metodę dla usługi - aktualizacja. Nie będziemy rozważać warstwy dao, ponieważ jest ona naszą domyślną. Dodajmy starter do testów:<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
Zatem klasa usług:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
8 - wyciągnij zaktualizowany obiekt z bazy danych 9-14 - utwórz obiekt za pomocą konstruktora, jeśli przychodzący obiekt ma pole - ustaw je, jeśli nie - zostaw to, co jest w bazie danych I spójrz na nasz test:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
1 — nasz Runner 4 — odizoluj usługę od warstwy dao, podstawiając próbkę 11 — ustaw jednostkę testową dla klasy (tę, której będziemy używać jako chomika testowego) 22 — ustaw obiekt usługi, który będziemy testować
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
Widzimy tutaj wyraźny podział testu na trzy części: 3-9 – ustawienie urządzeń 11 – wykonanie testowanej części 13-17 – sprawdzenie wyników Więcej szczegółów: 3-4 – ustawienie zachowania moka dao 5 – ustawienie instancja, którą zaktualizujemy na bazie naszego standardu 11 - użyj metody i weź wynikową instancję 13 - sprawdź, czy nie jest równa zero 14 - sprawdź identyfikator wyniku i podane argumenty metody 15 - sprawdź, czy nazwa została zaktualizowana 16 - spójrz na wynik dla procesora 17 - ponieważ nie ustawiliśmy tego w polu instancji aktualizacji, powinno pozostać takie samo, sprawdźmy to. Zacznijmy: Test jest zielony, można odetchnąć)) Podsumujmy: testowanie poprawia jakość kodu i sprawia, że proces programowania jest bardziej elastyczny i niezawodny. Wyobraź sobie, ile wysiłku musielibyśmy włożyć w przeprojektowanie oprogramowania zawierającego setki plików klas. Kiedy już mamy napisane testy jednostkowe dla wszystkich tych klas, możemy z pewnością przeprowadzić refaktoryzację. A co najważniejsze, pomaga nam łatwo znaleźć błędy podczas programowania. Chłopaki, to wszystko dla mnie dzisiaj: lajkujcie, piszcie komentarze)))
GO TO FULL VERSION