spock mocking stubbing
Kpiny, klepanie i szpiegowanie ze Spockiem:
Sparametryzowane testowanie w Spock Framework zostało szczegółowo wyjaśnione w tym dokumencie Seria samouczków szkoleniowych na temat Spocka .
Mocking i Stubbing to jedne z najważniejszych elementów składowych obszernych testów jednostkowych. Wsparcie dla kpiny i subbingu jest jak wisienka na torcie dla frameworka.
W przypadku istniejących frameworków, takich jak JUnit, JBehave itp., Wsparcie dla makiet i pośredników nie wychodzi z pudełka, dlatego wymaga od programisty korzystania z bibliotek innych firm, takich jak Mockito, PowerMock, EasyMock itp., Aby używać ich w testy jednostkowe.
Aby zrozumieć mocks i stubs oraz ich przypadki użycia, możesz zapoznać się z naszą serią Samouczek Mockito .
W tym samouczku dowiemy się więcej o wbudowanych funkcjach Mocking i Stubbing zintegrowanych z samą biblioteką Spock, co z kolei umożliwiłoby użycie łatwiejszej składni Groovy, a tym samym zmniejszyło potrzebę dodawania / dołączania jakichkolwiek innych 3r & Dbiblioteki partyjne.
Zawsze możesz dołączyć inne frameworki Mocking do swoich testów, ponieważ cały prawidłowy kod Java jest również prawidłowym kodem Groovy.
Czego się nauczysz:
- Aplikacja w trakcie testu
- Kpiny w Spocku
- Stubbing w Spocku
- Szpiegostwo w Spocku
- Wniosek
- Kod źródłowy aplikacji
- rekomendowane lektury
Aplikacja w trakcie testu
Najpierw zdefiniujmy przykładową aplikację Java, którą będziemy testować przy użyciu makiet i kodów pośredniczących we frameworku Spock.
Będziemy pracować nad aplikacją StudentGradeCalculator, która pobiera całkowity wynik z abstrakcyjnej bazy danych dla danej legitymacji studenckiej i ma prostą logikę przypisywania ocen w zależności od wartości łącznej oceny. Użyjemy interfejsu bazy danych, który ma kilka metod pobierania i aktualizowania wyników i ocen uczniów.
Kod aplikacji będzie dostępny w ostatniej sekcji tego samouczka.
Kpiny w Spocku
Instrukcja wideo
W tej sekcji zobaczymy, jak utworzyć instancję i zainicjować Mocks we frameworku Spock oraz jak sprawdzić poprawność interakcji na makiecie, tj. Sprawdzanie poprawności wywołań makiet odbyło się zgodnie z oczekiwaniami testowanej metody.
Dzięki Mockom nie musisz wykonywać wielu konfiguracji, ale możesz zweryfikować interakcje, które miały miejsce z pozorowanymi obiektami dostarczonymi do testowanej aplikacji.
Dzięki próbom możesz na przykład:
- Jakimi argumentami przywoływano drwiny?
- Jaka była całkowita liczba wezwań itp.?
- Ustalanie kolejności drwin.
Zobaczmy prosty przykład StudentGradeCalculator, w którym dostarczamy mockowany obiekt implementacji bazy danych i weryfikujemy interakcje z Mockiem. Spróbujemy zrozumieć fałszywe funkcje na prostych przykładach.
Należy pamiętać, że wszystkie walidacje interakcji powinny odbywać się w bloku „to” według konwencji.
Poniżej znajduje się kod testowanej metody (co zostanie nazwane w „ gdy: ”Blok)
public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; }
# 1) Weryfikacja interakcji za pomocą dokładnych argumentów: najpierw sprawdźmy interakcje z dokładnie oczekiwanymi argumentami. Tutaj będziemy oczekiwać, że wyreżyserowane metody zostaną wywołane z dokładnymi argumentami (zgodnie z przepływem wykonania metody).
Tutaj “ studentDatabase ”To Mock interfejsu bazy danych, dla którego sprawdzamy interakcje.
def 'illustrate mocks for interaction verification with arguments'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade('123','C') 1*studentDatabase.getStudentGrade('123') }
Jak pokazano powyżej, sprawdzamy poprawność za pomocą dokładnych argumentów, więc mockowana implementacja musiała zostać wywołana za pomocą. Wszelkie zmiany tych argumentów spowodują niepowodzenie testu, a dziennik błędów pokaże odpowiednią przyczynę.
Spróbujmy zmienić ocenę w „ updateStudentGrade ”Na„ A ”zamiast faktycznie nazywanego„ C ”i zobacz, jaki błąd otrzymamy po wykonaniu testu.
Too few invocations for: 1*studentDatabase.updateStudentGrade('123','A') (0 invocations) Unmatched invocations (ordered by similarity): 1 * studentDatabase.updateStudentGrade('123', 'C') 1 * studentDatabase.getStudentScores('123')
Wyświetli błąd, taki jak „Za mało wywołań”, ponieważ nie może znaleźć wywołania Mock z podanymi argumentami.
#dwa) Zobaczmy teraz, jak sprawdzić poprawność interakcji Mock bez podawania rzeczywistych wartości argumentów, tj. Interesuje nas tylko wiedza, że makieta została wywołana w metodzie, ale nie z jakim argumentem.
Tego typu wymagania są najczęściej spotykane podczas pisania testów jednostkowych dla rzeczywistego kodu produkcyjnego, ponieważ nie zawsze jest łatwo zidentyfikować rzeczywiste argumenty, które zasadniczo zależą od podstawowej logiki biznesowej testowanej aplikacji.
Składnia jest prosta, wystarczy użyć podkreślenia „_” dla argumentu, którego rzeczywista wartość nie jest znana.
Na przykład, aby sprawdzić dowolną wartość ciągu, możesz po prostu wspomnieć „_ Jako ciąg znaków ”W miejscu argumentu w teście i powinien zostać przekazany dla dowolnej wartości typu String (podobnie dla innych pierwotnych i niestandardowych typów danych).
Zrozummy to na przykładzie
def 'illustrate mocks for interaction verification with generic matchers'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) 1*studentDatabase.getStudentGrade('123') }
Ważną kwestią, na którą należy tutaj zwrócić uwagę, jest to, że zawsze możesz mieszać i dopasowywać dla tego, jakie argumenty są znane, a co nie. Na przykład w poniższym przykładzie sprawdzamy interakcję jednego modelu z rzeczywistymi argumentami, a drugiego z luźnymi dopasowaniami.
# 3) Na koniec zobaczmy scenariusz, w którym możemy ustalić kolejność wywołań próbnych, tj. W jakiej kolejności zostały wywołane makiety podczas wykonywania testu.
Czasami ważne jest, aby sprawdzić przepływ zdarzeń, gdy w testowanej aplikacji uczestniczy wielu współpracowników / makiet. Warto też zrozumieć i sprawdzić, czy metody były wywoływane we wcześniej ustalonej kolejności.
def 'illustrate mocks for validating order'() { when: studentReportGenerator.calculateStudentGrade('123'); then: 1*studentDatabase.getStudentGrade('123') then: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) }
Można to osiągnąć po prostu używając wielu bloków „then:” w kolejności zgodnej z oczekiwaniami sekwencji Mock. Jeśli wspomniana sekwencja nie spełnia rzeczywistej kolejności wywołań, generowany jest błąd wyszczególniający „Niewłaściwa kolejność wywołań”.
Na przykład, jeśli zmienię kolejność powyższych następnie instrukcji, wykonanie testu zwróci błąd, jak pokazano poniżej.
Wrong invocation order for: 1*studentDatabase.updateStudentGrade(_ as String, _ as String) (1 invocation) Last invocation: studentDatabase.updateStudentGrade('123', 'C')
Stubbing w Spocku
Instrukcja wideo
Omówiliśmy wszystko na temat mockowania, a teraz zobaczmy, jak definiować kody pośrednie w mockowanych obiektach. Stubbing to nic innego jak konfigurowanie wstępnie zdefiniowanych lub gotowych odpowiedzi na wywołania Mock w celu przetestowania różnych przepływów / scenariuszy testowanej aplikacji.
Potraktuj to jako programowanie makiety, która zwraca wstępnie zdefiniowaną wartość, gdy została wywołana. Będziemy kontynuować pracę z tą samą aplikacją StudentGradeCalculator i zablokować wywołania interfejsu bazy danych, aby przetestować różne scenariusze.
Stub jest jak Mock, który w pewien sposób naśladuje zachowanie prawdziwego obiektu. Możesz po prostu nazwać to zaprogramowanym Mockiem.
Stubbing składnia
Składnia kodu stubbingu to 2 operatory przesunięcia w prawo - tj. „ >> '
Aby ustawić skrót dla dowolnego połączenia, możesz zdefiniować go w następujący sposób:
StubbedObject.StubbedMethod(//argumentList) >> “Stubbed Response”
Przyjrzyjmy się teraz różnym scenariuszom z przykładami.
# 1) Stubbing z aktualnymi parametrami: Jeśli argumenty są znane z góry lub jeśli chcesz ustawić skrót tylko wtedy, gdy wywołanie ma określone argumenty, możesz użyć tego sposobu określania kodów pośredniczących.
def 'illustrate stubs with exact matchers'() { given: studentDatabase.getStudentScores('123') >> [20F, 30F, 50F] when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' }
Tutaj możesz zobaczyć, że kod pośredniczący został ustawiony z dokładnym argumentem, tj. StudentId w tym przypadku jako „123” (dla każdej innej wartości kod pośredniczący nie zostanie wywołany i zostanie zwrócona domyślna odpowiedź).
jacy są inni dostawcy poczty e-mail
# 2) Stubbing z pobłażliwymi dopasowaniami: Jeśli argumenty nie są znane (lub nie są ważne), możemy wspomnieć o nich luźno, tak jak w przypadku mocków, a składnia pozostaje taka sama, czyli podkreślenie „_”.
def 'illustrate stubs with loose matchers'() { given: studentDatabase.getStudentScores(_ as String) >> [20F, 30F, 10F] when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' }
# 3) Zobaczmy kolejny szybki przykład, w którym skonfigurowaliśmy kod pośredniczący, aby zgłosić wyjątek.
Te scenariusze są bardzo przydatne do weryfikacji logiki obsługi błędów testowanej aplikacji (tak jak w prawdziwym świecie, generowanie wszystkich wyjątków w rzeczywistości nie jest możliwe, ale można skonfigurować prosty kod pośredniczący, aby zwracał dowolny wyjątek, a następnie potwierdzał go w następnie blok).
def 'illustrate stubs with exceptions thrown'() { given: studentDatabase.getStudentScores(_ as String) >> {throw new RuntimeException()} when: studentReportGenerator.calculateStudentGrade('123') then: thrown(RuntimeException.class) }
Szpiegostwo w Spocku
Szpiedzy bazują na prawdziwych przedmiotach tj. potrzebują implementacji interfejsu, a nie samego abstrakcyjnego interfejsu. Szpiedzy są potężni i mogą pozwolić ci uzyskać rzeczywiste metody wywoływane dla testowanej aplikacji i zweryfikować, do jakich argumentów te metody zostały przywołane.
Szpiedzy pozwalają także na definiowanie częściowych mocków na instancjach szpiegowanych obiektów. tj. przypuśćmy, że chcesz zdefiniować zachowanie niektórych metod na obiekcie, a następnie możesz i pozwolić na wywołanie pozostałych metod jako rzeczywistych wywołań metod.
Są one zwykle przydatne w sytuacji, gdy niektóre metody interfejsu mogą nie zostać zaimplementowane, a kilka innych jest w pełni funkcjonalnych. Dlatego jako programista możesz zdecydować się na odrzucenie niezaimplementowanych i wywołanie prawdziwych implementacji metod funkcjonalnych.
Należy zauważyć, że w przypadku obiektów szpiegowanych, o ile nie zdefiniowano kodów pośredniczących, domyślnym zachowaniem będzie wywołanie rzeczywistej implementacji. Mimo to, szpiegów nie należy często wzywać, a całość pokrycia scenariusza można uzyskać za pomocą fałszywych kodów i ich kombinacji.
Zobaczmy kilka przykładów użycia programu Spies we frameworku Spock na tym samym przykładzie StudentGradeCalculator (Stworzyliśmy prawdziwą implementację StudentDatabase który jest implementacją w pamięci przy użyciu HashMap aby zilustrować wywoływanie prawdziwych metod i zwracanie danych. Kod będzie dostępny w ostatniej sekcji samouczka):
# 1) Szpiegowanie przy użyciu kombinacji wywołań metod pośrednich i rzeczywistych
def 'illustrate spies'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'A' 1*spiedStudentDatabase.getStudentGrade(_ as String) >> 'A' }
Powyższy przykład ilustruje składnię tworzenia Spy przy użyciu frameworka Spock. Kod pośredniczący jest definiowany w samym czasie deklaracji.
Ponadto szpiegowane wywołania można zweryfikować, jak pokazano w bloku then (z dopasowaniem luźnych argumentów, które można zdefiniować dla dowolnych określonych argumentów).
# 2) Szpiegowanie przy użyciu wszystkich prawdziwych wywołań metod
def 'illustrate spies with real method call'() { given: StudentDatabase spiedStudentDatabase = Spy(StudentDatabase.class) def studentReportGenerator = new StudentReportGenerator(spiedStudentDatabase) when: def grade = studentReportGenerator.calculateStudentGrade('123') then: grade == 'C' 1*spiedStudentDatabase.getStudentGrade('123') }
W powyższym przykładzie, ponieważ nie wspomnieliśmy o żadnym zachowaniu typu „stubbed”, wszystkie wywołania będą kierowane do rzeczywistej implementacji.
Wniosek
W tym samouczku dowiedzieliśmy się wszystkiego o wbudowanych technikach Mock Stub and Spy przy użyciu frameworka Spock. Spock ułatwia to, łącząc te funkcje jako część samego frameworka z bardziej czytelną, groovy składnią wraz z mniejszym standardowym kodem.
Mocks, Stubs i Spies są szeroko stosowane w testach jednostkowych w celu zwiększenia zasięgu i testowania lub walidacji podstawowej logiki biznesowej testowanej aplikacji.
Kod źródłowy aplikacji
StudentReportGenerator.java - jest to testowana metoda / aplikacja
package app.studentScores; import java.util.List; public class StudentReportGenerator { public IStudentDatabase studentDatabase; public StudentReportGenerator(IStudentDatabase studentDatabase) { this.studentDatabase = studentDatabase; } public String calculateStudentGrade(String studentId) { String grade; // check if grade is already there in database grade = studentDatabase.getStudentGrade(studentId); if(grade!=null && !grade.isEmpty()) { return grade; } List scoreList = studentDatabase.getStudentScores(studentId); Float totalScore = 0F; if(scoreList !=null) totalScore = scoreList.stream().reduce(0F,(a,b)->a+b); if(totalScore > 90) { grade = 'A'; } else if (totalScore > 80) { grade = 'B'; } else { grade = 'C'; } // update the calculated grade in database studentDatabase.updateStudentGrade(studentId, grade); return grade; } }
IStudentDatabase.java - Interfejs bazy danych
package app.studentScores; import java.util.List; public interface IStudentDatabase { List getStudentScores(String studentId); void updateStudentGrade(String studentId, String grade); String getStudentGrade(String studentId); }
StudentDatabase.java - implementacja InMemory interfejsu IStudentDatabase.java
package app.studentScores; import java.util.*; public class StudentDatabase implements IStudentDatabase { private Map scoreMap; private Map gradeMap; public StudentDatabase() { this.scoreMap = new HashMap(); this.gradeMap = new HashMap(); scoreMap.put('123', Arrays.asList(40F, 30F, 30F)); scoreMap.put('456', Arrays.asList(10F, 10F, 30F)); gradeMap.put('123', 'C'); gradeMap.put('456', 'A'); } @Override public List getStudentScores(String studentId) { return scoreMap.get(studentId); } @Override public void updateStudentGrade(String studentId, String grade) { gradeMap.put(studentId,grade); } @Override public String getStudentGrade(String studentId) { return gradeMap.get(studentId); } }
W naszym nadchodzącym samouczku zobaczymy, jak zintegrować framework Spock z innymi frameworkami testowymi i technologiami.
POPRZEDNIA samouczek | NEXT Tutorial
rekomendowane lektury
- Pisanie testów jednostkowych w Spock Framework
- Pytania do wywiadu Spocka z odpowiedziami (najpopularniejsze)
- Spock do integracji i testów funkcjonalnych z selenem
- Testowanie oparte na danych lub parametryzowane za pomocą Spock Framework
- Samouczek Spocka: Testowanie ze Spockiem i Groovym
- Najlepsza DARMOWA seria samouczków języka C #: najlepszy przewodnik po języku C # dla początkujących
- Testowanie obciążenia za pomocą samouczków HP LoadRunner
- Funkcje daty i czasu w C ++ z przykładami