Jak napisać pierwszy test dla Rest Api w Javie?
Z dzisiejszego artykułu, drugiego z serii poświęconym testom REST API dowiesz się:
- Czym jest RestAssured
- Jak utworzyć nowy projekt do testowania Rest Api?
- W jaki sposób przygotować proste testy weryfikujące działanie Endpointów w Twojej aplikacji?
Spis treści
Czym jest RestAssured?
RestAssured jest to biblioteka pozwalająca na przesyłanie żądań oraz analizę odpowiedzi w trakcie komunikacji z Rest Api. Czyli pozwala to na testowanie poprawności Rest Api (więcej o testowaniu Rest Api znajdziesz >tutaj<).
Biblioteka przeznaczona jest do użytku wraz z językiem programowania Java, a jej autorem i głównym kontrybutorem jest Johan Haleby. Bibliotekę najłatwiej jest pobrać z strony mvnrepository.com
Jak utworzyć nowy projekt do testowania Rest Api?
io.rest-assured
rest-assured
5.3.0
test
org.testng
testng
7.7.1
test
RestAssured tutorial czyli jak napisać pierwszy test korzystając z RestAssured?
Dla kogoś kto zetknął się z testami Selenium, czy testami jednostkowymi, nie ma tu większej zagwozdki – standardowo tworzymy nową klasę w ścieżce src/test/java, a następnie tworzymy nową metodę publiczną, gdzie umieszczamy kod testowy:
@Test
public void getFirstUserDataTest(){
given()
.when()
.get("https://reqres.in/api/users/1")
.then()
.statusCode(200);
}
Powyższy test powoduje wysłanie żądania (requestu) do https://reqres.in/api/users/1, a następnie weryfikuje, czy serwer ten zwrócił właściwy kod odpowiedzi, tj. 200 w tym przypadku.
Z czego składa się test w RestAssured?
Gdy przyjrzymy się przytoczonemu wcześniej przykładowi to dostrzeżemy pewien schemat z zwinnego podejścia BDD (Behaviour driven development) czyli typową składnie Gherkina – given, when, then…
Given...
- pozwala na zdefiniowane strony bazowej (tzw. BaseURL) czyli np. https://reqres.in/api
- pozwala na konfigurację przesyłania danych takich jak drukowanie logów czy filtrację danych
- pozwala na przesłanie dodatkowych parametrów oraz headerów w requeście (doprecyzowanie requestu)
When…
stanowi słowo kluczowe w połączeniu wszystkich fraz
- jest to ten krok, po którym dochodzi do wykonania głównej akcji będącej elementem testu takiej jak np. przesłanie żądania typu get/delete/post itd.
Then...
weryfikująca część metody testowej
inicjuje wywołanie metod weryfikujących takie parametry jak:
- kod odpowiedzi z serwera
- nagłówki odpowiedzi serwera
- weryfikację wybranej części ciała odpowiedzi z serwera
Weryfikacja ciała odpowiedzi z serwera
Przyjrzyjmy się kolejnemu przykładowi – tym razem z odpowiedzi serwera chcemy wyciągnąć i porównać nazwisko użytkownika z numerem id=2.
Żądanie do serwera:
GET https://reqres.in/api/users/2
Odpowiedź z serwera:
{
"data": {
"id": 2,
"email": "janet.weaver@reqres.in",
"first_name": "Janet",
"last_name": "Weaver",
"avatar": "https://reqres.in/img/faces/2-image.jpg"
},
"support": {
"url": "https://reqres.in/#support-heading",
"text": "To keep ReqRes free, contributions towards server costs are appreciated!"
}
}
Interesujące nas dane przechowywane są w węźle data->last_name.
Najprostszy sposób, aby się do nich dostać to bezpośrednie wskazanie ścieżki w ramach metody body („data.last_name„).
Następnie pozostanie już tylko przyrównać to co otrzymamy do oczekiwanej przez nas wartości – tu z pomocą przyjdzie statyczny matcher ‘equalTo()’, który porównuje czy obie wartości są identyczne.
Finalnie nasz test przyjmie następującą formę:
@Test
public void verifySecondUserLastNameTest(){
given()
.baseUri("https://reqres.in/api")
.pathParam("userId", 2)
.when()
.get("/users/{userId}")
.then()
.body("data.last_name", equalTo("Weaver"));
}
Co z innymi rodzajami żądań?
Rozpatrzmy kolejny test – w tym przykładzie prześlemy dodatkowy nagłówek żądania w celu wylistowania go przez serwer w węźle ‘headers’
Żądanie:
POST https://postman-echo.com/post
Ciało odpowiedzi z serwera (Body):
Ciało odpowiedzi serwera:
{
"args": {
},
"data": "",
"files": {
},
"form": {
},
"headers": {
"x-forwarded-proto": "https",
"x-forwarded-port": "443",
"host": "postman-echo.com",
"x-amzn-trace-id": "Root=1-6420a65a-750ea1065c66d36a46bf2e42",
"content-length": "0",
"content-type": "application/x-www-form-urlencoded",
"accept": "*/*",
"myheader": "tu_jest_bug",
"user-agent": "Apache-HttpClient/4.5.13 (Java/17.0.3.1)",
"accept-encoding": "gzip,deflate"
},
"json": null,
"url": "https://postman-echo.com/post"
}
Celem testu będzie zweryfikowanie, że istnieje pole ‘myheader’ w węźle ‘headers’ o wartości ‘tu_jest_bug’
@Test
public void verifyHeadersAreSent(){
given()
.baseUri("https://postman-echo.com")
.header("myheader", HEADER_VALUE)
.when()
.post("/post")
.then()
.statusCode(200)
.body("headers.myheader", equalTo(HEADER_VALUE));
}
Po uruchomieniu testu … otrzymamy błąd!
java.lang.AssertionError: 1 expectation failed.
Expected status code <200> but was <500>.
O ile nie posiedliśmy jakiejś tajemnej wiedzy, może być dość trudno wytypować dlaczego serwer zwraca błąd 500 – ponieważ nie otrzymaliśmy żadnej wiadomości.
Z pomocą przyjdą nam wcześniej wspomniane logi – czyli zapisy komunikacji pomiędzy nami a serwerem.
Logi w RestAssured są zapisywane zarówno dla żądań jak i dla odpowiedzi – wtedy gdy zdefiniujemy dodatkową akcję log() po :
- when() – zapisywanie logów z zapytań do serwera
- then() – zapisywanie logów z odpowiedzi z serwera
Po dodaniu logów nasz test zawiera dodatkowe 2 linie kodu:
@Test
public void verifyHeadersAreSent(){
given()
.baseUri("https://postman-echo.com")
.header("myheader", HEADER_VALUE)
.when()
.log().all()
.post("/post")
.then()
.log().all()
.statusCode(200)
.body("headers.myheader", equalTo(HEADER_VALUE));
}
Żądanie:
Request method: POST
Request URI: https://postman-echo.com/post
Proxy:
Request params:
Query params:
Form params:
Path params:
Headers: myheader=tu_jest_bug
Accept=*/*
Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1
Cookies:
Multiparts:
Body:
Odpowiedź:
HTTP/1.1 500 Internal Server Error
Date: Mon, 03 Apr 2023 06:40:57 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 428
Connection: keep-alive
ETag: W/"1ac-PU9dCX2qf/xFu9z0Bpbft2FRhB4"
set-cookie: sails.sid=s%3A-t-1qTRrGTqp-W_gELypreuk0pVe8G0_.4HeWA%2BE78M%2Fcw3ZgVd%2FfyTY9cTlfEihUWMocbelTOkU; Path=/; HttpOnly
{
"charset": "iso-8859-1",
"type": "charset.unsupported",
"level": "error",
"event": "unsupported charset \"ISO-8859-1\"",
"timestamp": 1680504057692,
"entity.name": "echo: production",
"entity.type": "SERVICE",
"hostname": "echo-deployment-1-84fc8c6c74-42xbh",
"trace.id": "2ddc02d74d578cbacf4096543631f1d4",
"span.id": "840a950a15a5b7ca",
"entity.guid": "MjY2NTkxOHxBUE18QVBQTElDQVRJT058MTQxNzMwNTI4Ng"
}
Po przeanalizowaniu logów widać jak na dłoni w czym jest problem!
RestAssured przesyła do serwera zapytanie kodując je w domyślnym charsecie dla języka polskiego ISO-8859-1, który nie jest obsługiwany przez sewer postman-echo.com
Aby temu zapobiec i wymusić kodowanie w UTF-8 wspieranym przez postman-echo.com będziemy musieli dodać dodatkową linię kodu dotyczącą konfigracji:
@Test
public void verifyHeadersAreSent(){
given()
.config(config().encoderConfig(EncoderConfig.encoderConfig()
.appendDefaultContentCharsetToContentTypeIfUndefined(false)))
.baseUri("https://postman-echo.com")
.header("myheader", HEADER_VALUE)
.when()
.log().all()
.post("/post")
.then()
.log().all()
.statusCode(200)
.body("headers.myheader", equalTo(HEADER_VALUE));
}
Po uruchomieniu w/w kodu powinniśmy zobaczyć header ‘tu_jest_bug’ w odpowiedzi z serwera, a sam też zakończy się sukcesem. Dobra robota!
Co będziemy weryfikować korzystając z RestAssured?
Za testową część RestAssured odpowiada kod poprzedzony słowem then(). Jak pokazałem w poprzednich przykładach najczęściej weryfikować będziemy:
- Kod odpowiedzi serwera
- 2XX – np. 200, oznaczające, że wszystko poszło OK, może to też być taki pierwszy ‘check’ jak pokazano w przykładzie trzecim, po którym wykonujemy pozostałe testy
- 4XX – np. 404, oznacza, że żądanie klienta jest niepoprawne. Wbrew pozorom są to istotne, a często pomijane testy, które pozwalają upewnić się, że ‘backend’ wie co robi i odpowiednio obsługuje niepoprawne żądania
- Ciało odpowiedzi z serwera
- Tutaj najczęściej szukamy konkretnych wartości, które chcemy porównać z oczekiwanymi danymi
- Zdarza się, że z odpowiedzi z serwera chcemy wyciągnąć konkretną wartość, np. id jakiegoś zasobu, aby wysłać kolejne żądanie do innego endpointu aplikacji w celu dalszych testów
- Nagłówki odpowiedzi z serwera czyli ‘Headery’
- Czas odpowiedzi
- Inne 🙂
A poniżej możesz zobaczyć teorię w praktyce Jak napisać pierwszy test Rest Api w Javie?
Zapraszam do oglądania i subskrybowania kanału @tujestbug
Najnowsze materiały w postaci artykułów i filmików oraz info o wydarzeniach testerskich/warsztatach w których biorę udział znajdziesz również na Facebooku
Jeśli podobał Ci się artykuł, zostaw komentarz i jeśli tego jeszcze nie zrobiłeś to koniecznie dopisz się do listy subskrybentów, aby nie ominęły Cię kolejne artykuły z serii o RestAssured.
Pozdrawiam!
Piotr
P.S A jeśli jesteś szczególnie zainteresowny tym tematem to zapraszam Cie na najbliższe szkolenie z testowania Rest API, które poprowadzę już 1 i 2 czerwca dla testerzy.pl!
0 komentarzy