writing unit tests with spock framework
Pisanie testów jednostkowych w Spock Framework: Test Fixtures, Assertions and Reporting
W tym Kompletny przewodnik dla początkujących w Spocku , krótki Wprowadzenie do Spock Framework i programowania Groovy zostało podane w naszym poprzednim samouczku.
W tym samouczku omówimy wszystkie szczegóły / kroki wymagane do rozpoczęcia Testów jednostkowych w Spocku.
Dla uproszczenia zamierzamy przetestować prostą aplikację kalkulatora, która ma różne metody, takie jak dodawanie, odejmowanie, mnożenie, dzielenie itp., Z których wszystkie akceptują parametry całkowite i zwracają wynik w postaci liczb całkowitych.
Czego się nauczysz:
- Testowanie jednostkowe za pomocą samouczka wideo Spock
- Pierwsze kroki
- Słowo kluczowe „def”
- Cykl życia specyfikacji Spocka
- Asercje Spocka
- Raportowanie
- Wniosek
- rekomendowane lektury
Testowanie jednostkowe za pomocą samouczka wideo Spock
Pierwsze kroki
Podobnie jak inne frameworki do testów jednostkowych, Spock również może być używany do pisania scenariuszy / przypadków testowych dla testowanej aplikacji. Spróbujemy porównać i zestawić różne funkcje frameworka Spock z istniejącymi / znanymi frameworkami, takimi jak JUnit .
Słowo kluczowe „def”
Najpierw spróbujmy w skrócie zrozumieć słowo kluczowe „def” Groovy'ego. Słowo kluczowe def jest używane do definiowania type-def i może być używane do deklarowania funkcji, a także pola / zmiennej.
„Def” jest zwykle używane, gdy nie chcemy ograniczać typu pola lub zwracanego typu metody. Zobaczmy kilka przykładów słowa kluczowego def w groovy klasy i wszystkie jego prawidłowe zastosowania.
// def as variable types def inputNum = 100 def inputStr = 'hello world!!' def app = new CalculatorApp() // def as return type of function def 'test function'() { // function body here }
Cykl życia specyfikacji Spocka
Spock spec po wykonaniu wyszukuje wszystkie zdefiniowane testy i wykonuje je jeden po drugim. Jednak jest kilka innych funkcji / cech, które są dostarczane przez Spocka, aby testy były mniej zbędne i bardziej czytelne.
Omówmy poniżej niektóre funkcje:
Definiowanie wejść / zmiennych jako część specyfikacji
Rozważ przeprowadzenie wielu testów przy użyciu tych samych wartości wejściowych. Jednym ze sposobów byłoby zainicjowanie wartości wejściowych w każdym teście indywidualnie, w przeciwnym razie możemy bezpośrednio zdefiniować pola na poziomie specyfikacji i upewnić się, że przed każdym testem pola zostaną zainicjowane i będą dostępne dla wykonywanego testu.
Zobaczmy przykład dla naszej klasy aplikacji kalkulatora .
Zdefiniujemy dane wejściowe na poziomie specyfikacji, aby były dostępne z wartościami początkowymi dla wszystkich testów obecnych w specyfikacji.
class CalculatorAppSpec extends Specification { def input1 = 50 def input2 = 10 def result = 0 def app = new CalculatorApp() def 'addition with valid inputs return expected result'() { when: result = app.add(input1, input2) then: result == 60 } def 'multiplication with valid inputs return expected result'() { when: result = app.multiply(input1, input2) then: result == 500 } def 'division with valid inputs return expected result'() { when: result = app.divide(input1, input2) then: result == 5 } def 'subsctraction with valid inputs return expected result'() { when: result = app.substract(input1, input2) then: result == 40 } }
W tym przykładzie kodu widać, że zdefiniowaliśmy input1, input2, testowaną aplikację i wynik na poziomie specyfikacji. Gwarantuje to, że za każdym razem, gdy test jest uruchamiany z plików specyfikacji, a zainicjowane pola są przekazywane do tego testu. To faktycznie eliminuje potrzebę konfigurowania testów za każdym razem z wartościami wejściowymi.
Test Fixtures
Podobnie jak większość struktur testowania jednostkowego, Spock zapewnia również metody konfiguracji i czyszczenia dla wykonywania specjalnej logiki / zadań w określonych zdarzeniach cyklu życia wykonywania testów.
setupSpec & cleanupSpec
Te metody są wywoływane raz dla każdego wykonania Spec i są wywoływane odpowiednio przed i po wykonaniu testu. Są one porównywalne z @ Przed zajęciami i @ Po zajęciach adnotacje JUnit.
konfiguracja i czyszczenie
Metody te są wywoływane przed i po wykonaniu każdego testu w specyfikacji.
Te punkty zaczepienia są właściwym miejscem dla dowolnej logiki / fragmentu kodu, który chciałbyś wykonać przed i po wykonaniu testu. Na przykład , Podczas czyszczenia możesz napisać kod zamykający połączenie z bazą danych (jeśli takie istnieje), które zostało użyte podczas testu.
Można je porównać do @ Przed testem i @ AfterTest adnotacje w JUnit.
Zobaczmy przykład tych urządzeń w naszym teście aplikacji kalkulatora.
def setupSpec() { println('###in setup spec!') } def cleanupSpec() { println('###in cleanup spec!') } def setup() { println('>>>in test setup!') } def cleanup() { println('>>>in test cleanup!') }
Jeśli powyższy kod urządzenia testowego zostanie dodany do specyfikacji zawierającej 4 testy, wynik będzie następujący:
###in setup spec! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! >>>in test setup! >>>in test cleanup! ###in cleanup spec!
Asercje Spocka
Asercje w Spocku nazywane są asercją mocy (i zostało przyjęte przez groovy później po wprowadzeniu przez Spocka). Asercje Spocka zapewniają wiele wyjątków diagnostycznych na wypadek jakichkolwiek niepowodzeń asercji.
Można łatwo dowiedzieć się, co poszło nie tak, po prostu patrząc na diagnostykę awarii, a nie na szczegółową AssertionErrors w JUnit i innych frameworkach.
Spróbujmy to zrozumieć na przykładzie i porównajmy to z JUnit
Będziemy pracować z prostym testem, który sprawdza równość ciągów i sprawdzamy, jakie komunikaty diagnostyczne są generowane w przypadku niepowodzenia asercji.
Test Spocka
def 'check case-insensitive equality of 2 strings'() { given: 'two input strings' String str1 = 'hello' String str2 = 'HELLO world' when: 'strings are lowercased' str1 = str1.toLowerCase() str2 = str2.toLowerCase() then: 'equal strings should return success' str1 == str2 }
JUnit Test
@Test public void compareStrings_withValidInput_shouldReturnSuccess() { // Arrange String str1 = 'hello'; String str2 = 'HELLO world'; // Act str1 = str1.toLowerCase(); str2 = str2.toLowerCase(); // Assert Assert.assertEquals(str1,str2); }
Spock Output
Condition not satisfied: str1 == str2 | | | hello| hello world false 6 differences (45% similarity) hello(------) hello( world) Expected :hello world Actual :hello
Wyjście JUnit
org.junit.ComparisonFailure: Expected :hello Actual :hello world
Jak możesz wywnioskować z powyższego, informacje diagnostyczne dostarczone przez Spock są bardziej szczegółowe i są bardziej przyjazne dla użytkownika w porównaniu z innymi frameworkami, takimi jak JUnit.
Wskazówki i porady dotyczące asercji
Asserting wielu elementów na raz - Spock dostarcza różne skróty dla potwierdzeń, a jedną z nich jest notacja „*”, która pozwala na potwierdzanie elementów na liście.
Zrozummy to na przykładzie:
Rozważ klasę CityInfo mającą cityName i populację jako pola. Napiszemy test Spocka, aby potwierdzić nazwy miast, które znajdują się na podanej liście.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; }
Zobaczmy teraz test:
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ('Mumbai', 'Delhi', 'Chennai') }
Jak pokazano w powyższym skrócie asercji, całą listę można zweryfikować za pomocą słowa kluczowego „*”.
najlepsze oprogramowanie vm dla systemu Windows 10
Zobaczmy też, jak wyglądałaby awaria. Usunę nazwę dowolnego miasta z powyższego stwierdzenia.
Condition not satisfied: cityList*.cityName == ('Delhi', 'Chennai') | | | | | false | (Mumbai, Delhi, Chennai) (app.CityInfo@31368b99, app.CityInfo@1725dc0f, app.CityInfo@3911c2a7)
Jak widać, informacje diagnostyczne o niepowodzeniu asercji są bogate i łatwe do zrozumienia.
Dźwignia parametru domknięcia - every ().
Zobaczmy, jak możemy wykorzystać parametr zamknięcia o nazwie every (), aby dodać potwierdzenie dla każdego elementu listy lub kolekcji. W tym samym przykładzie spróbujmy dodać asercję, która weryfikuje populację każdego miasta, jeśli dane wejściowe są> 50.
def 'Assert multiple elements of list' () { given: def cityList = new LinkedList() cityList.add(new CityInfo('Mumbai', 120)) cityList.add(new CityInfo('Delhi', 80)) cityList.add(new CityInfo('Chennai', 100)) expect: cityList*.cityName == ('Mumbai', 'Delhi', 'Chennai') and: cityList.population.every() { it > 50 } }
Potwierdzanie wyjątków zgłaszanych
Można stwierdzić, że wyjątki są wrzucane w bloku „to” (co oznacza, że blok jest również wymagany). Szczegóły wyjątku można zdiagnozować, przypisując zgłoszony wyjątek do pola i potwierdzając wymagane właściwości zgłoszonego wyjątku.
Użyjmy tej samej klasy CityInfo i zdefiniujmy metodę, która zgłasza wyjątek i napiszemy dla niej test.
public class CityInfo { public CityInfo(String cityName, int population) { this.cityName = cityName; this.population = population; } public String cityName; public int population; public CityInfo() { } public int getCleanlinessScore() { throw new RuntimeException('method not implemented'); } }
Spójrzmy teraz na test:
def 'cleanliness score throws runtime exception with message - method not implemented'() { given: CityInfo app = new CityInfo(); when: app.cleanlinessScore() then: def e = thrown(RuntimeException) e.message == 'method not implemented' }
Raportowanie
Aby generować piękne i szczegółowe raporty w formacie HTML, dostępne są biblioteki, które można dodać do pliku kompilacji, a teraz za każdym razem, gdy testy są wykonywane podczas kompilacji (lub przez bezpośrednie wykonanie), szczegółowy raport oparty na html zostanie wygenerowany w folder wyjściowy.
Aby uzyskać wygenerowane raporty z testów, dodaj następujące biblioteki do pliku build.gradle (i podobnie do pliku pom.xml Mavena).
testCompile 'com.athaydes:spock-reports:1.6.1' testCompile 'org.slf4j:slf4j-api:1.7.13' testCompile 'org.slf4j:slf4j-simple:1.7.13'
Teraz skompiluj projekt i wykonaj testy, uruchamiając wszystkie testy w folderze „test” lub wykonując „ test czystości ”.
Możesz otworzyć index.html plik, aby uzyskać podsumowanie raportu dla wszystkich specyfikacji Spocka, które były dostępne do wykonania.
Jeśli chcesz zobaczyć szczegółowy raport dla określonej Specyfikacji, kliknij specyfikację z powyższej listy, a zobaczysz szczegółowy raport zarówno niepowodzeń, jak i sukcesów.
Wniosek
W tym samouczku omówiliśmy podstawy testowania jednostkowego w Spock Framework. Widzieliśmy różne sposoby i skróty pisania asercji oraz rodzaj bogatych informacji diagnostycznych generowanych przez platformę Spock dla niepowodzeń asercji.
Przyjrzeliśmy się również, jak możemy generować ciche, ładne raporty HTML dla testów Spock, które zawierają tę samą szczegółową diagnostykę dla wykonanych testów.
Nasz nadchodzący samouczek zawiera szczegółowe informacje na temat pisania testów parametryzowanych w Spocku !!
POPRZEDNIA samouczek | NEXT Tutorial
rekomendowane lektury
- Testowanie oparte na danych lub parametryzowane za pomocą Spock Framework
- Pytania do wywiadu Spocka z odpowiedziami (najpopularniejsze)
- Spock do integracji i testów funkcjonalnych z selenem
- Spock Mocking and Stubbing (przykłady z samouczkami wideo)
- Samouczek Spocka: Testowanie ze Spockiem i Groovym
- Samouczek Mockito: Mockito Framework do mockowania w testach jednostkowych
- Różnice między testowaniem jednostkowym, testowaniem integracyjnym i testowaniem funkcjonalnym
- Klucz do udanego testowania jednostkowego - jak programiści testują swój własny kod?