Um Guia para Garantia de REST
1. Introdução
A garantia REST foi projetada para simplificar o teste e a validação das APIs REST e é altamente influenciada pelas técnicas de teste usadas em linguagens dinâmicas, como Ruby e Groovy.
A biblioteca tem suporte sólido para HTTP, começando naturalmente com os verbos e operações HTTP padrão, mas também indo muito além desses princípios.
Neste guia, vamosexplore REST-assurede vamos usar o Hamcrest para fazer asserções. Se você ainda não está familiarizado com o Hamcrest, deve primeiro se atualizar com o tutorial:Testing with Hamcrest.
Além disso, para aprender sobre casos de uso mais avançados de garantia REST, confira nossos outros artigos:
Agora vamos mergulhar em um exemplo simples.
2. Teste de exemplo simples
Antes de começarmos, vamos garantir que nossos testes tenham as seguintes importações estáticas:
io.restassured.RestAssured.*
io.restassured.matcher.RestAssuredMatchers.*
org.hamcrest.Matchers.*
Precisamos disso para manter os testes simples e ter acesso fácil às principais APIs.
Agora, vamos começar com o exemplo simples - um sistema básico de apostas que expõe alguns dados para jogos:
{
"id": "390",
"data": {
"leagueId": 35,
"homeTeam": "Norway",
"visitingTeam": "England",
},
"odds": [{
"price": "1.30",
"name": "1"
},
{
"price": "5.25",
"name": "X"
}]
}
Digamos que esta seja a resposta JSON ao atingir a API implantada localmente -http://localhost:8080/events?id=390.:
Agora, vamos usar REST garantido para verificar alguns recursos interessantes da resposta JSON:
@Test
public void givenUrl_whenSuccessOnGetsResponseAndJsonHasRequiredKV_thenCorrect() {
get("/events?id=390").then().statusCode(200).assertThat()
.body("data.leagueId", equalTo(35));
}
Então, o que fizemos aqui foi - verificamos que uma chamada para o ponto final/events?id=390 responde com um corpo contendo umJSON String cujoleagueId do objetodata é 35.
Vamos dar uma olhada em um exemplo mais interessante. Digamos que você gostaria de verificar se a matrizodds tem registros com preços1.30e5.25:
@Test
public void givenUrl_whenJsonResponseHasArrayWithGivenValuesUnderKey_thenCorrect() {
get("/events?id=390").then().assertThat()
.body("odds.price", hasItems("1.30", "5.25"));
}
3. Configuração com REST garantida
Se sua ferramenta de dependência favorita é o Maven, adicionamos a seguinte dependência no arquivopom.xml:
io.rest-assured
rest-assured
3.0.0
test
Para obter a versão mais recente, sigathis link. O REST-assegurado aproveita o poder dos matchers do Hamcrest para realizar suas asserções, portanto, devemos incluir essa dependência também:
org.hamcrest
hamcrest-all
1.3
A versão mais recente estará sempre disponível emthis link.
4. Validação de raiz JSON anônima
Considere uma matriz que compreende primitivas em vez de objetos:
[1, 2, 3]
Isso é chamado de raiz JSON anônima, o que significa que não possui um par de valores-chave, no entanto, ainda são dados JSON válidos.
Podemos executar a validação em tal cenário usando o símbolo`$` ou uma String vazia (“”) como caminho. Suponha que exponhamos o serviço acima por meio dehttp://localhost:8080/json, então podemos validá-lo assim com REST garantido:
when().get("/json").then().body("$", hasItems(1, 2, 3));
ou assim:
when().get("/json").then().body("", hasItems(1, 2, 3));
5. Floats and Doubles
Quando começamos a usar REST garantido para testar nossos serviços REST, precisamos entender que os números de ponto flutuante nas respostas JSON são mapeados para o tipo primitivofloat.
O uso do tipofloat não é intercambiável comdouble como é o caso para muitos cenários em java.
O caso em questão é esta resposta:
{
"odd": {
"price": "1.30",
"ck": 12.2,
"name": "1"
}
}
suponha que estamos executando o seguinte teste no valor deck:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2));
Este teste falhará mesmo que o valor que estamos testando seja igual ao valor na resposta. Isso ocorre porque estamos comparando adouble em vez de afloat.
Para fazer isso funcionar, temos que especificar explicitamente o operando para o método matcherequalTo como umfloat, assim:
get("/odd").then().assertThat().body("odd.ck", equalTo(12.2f));
6. Especificando o Método de Solicitação
Normalmente, faríamos uma solicitação chamando um método comoget(), correspondente ao método de solicitação que desejamos usar.
Além disso,we can also specify the HTTP verb using the request() method:
@Test
public void whenRequestGet_thenOK(){
when().request("GET", "/users/eugenp").then().statusCode(200);
}
O exemplo acima é equivalente a usarget() diretamente.
Da mesma forma, podemos enviar solicitaçõesHEAD,CONNECTeOPTIONS:
@Test
public void whenRequestHead_thenOK() {
when().request("HEAD", "/users/eugenp").then().statusCode(200);
}
A solicitaçãoPOST também segue uma sintaxe semelhante e podemos especificarthe body usando os métodoswith() andbody().
Portanto, para criar um novoOdd , enviando uma solicitaçãoPOST :
@Test
public void whenRequestedPost_thenCreated() {
with().body(new Odd(5.25f, 1, 13.1f, "X"))
.when()
.request("POST", "/odds/new")
.then()
.statusCode(201);
}
OOdd objeto enviado comobody will será automaticamente convertido para JSON. Também podemos passar qualquerString que quisermos enviar como nossoPOSTbody.
7. Configuração de valores padrão
Podemos configurar muitos valores padrão para os testes:
@Before
public void setup() {
RestAssured.baseURI = "https://api.github.com";
RestAssured.port = 443;
}
Aqui, estamos definindo um URI básico e uma porta para nossas solicitações. Além desses, também podemos configurar o caminho base, o root root e a autenticação.
Nota: também podemos redefinir os padrões garantidos por REST padrão usando:
RestAssured.reset();
8. Medir o tempo de resposta
Vamos ver como podemosmeasure the response time using the time() and timeIn() methods of the Response object:
@Test
public void whenMeasureResponseTime_thenOK() {
Response response = RestAssured.get("/users/eugenp");
long timeInMS = response.time();
long timeInS = response.timeIn(TimeUnit.SECONDS);
assertEquals(timeInS, timeInMS/1000);
}
Observe que:
-
time() é usado para obter o tempo de resposta em milissegundos
-
timeIn() é usado para obter o tempo de resposta na unidade de tempo especificada
8.1. Validar Tempo de Resposta
Também podemos validar o tempo de resposta - em milissegundos - com a ajuda delongMatcher: simples
@Test
public void whenValidateResponseTime_thenSuccess() {
when().get("/users/eugenp").then().time(lessThan(5000L));
}
Se quisermos validar o tempo de resposta em uma unidade de tempo diferente, usaremos o matchertime() com um segundo parâmetroTimeUnit:
@Test
public void whenValidateResponseTimeInSeconds_thenSuccess(){
when().get("/users/eugenp").then().time(lessThan(5L),TimeUnit.SECONDS);
}
9. Verificação de resposta XML
Não apenas pode validar uma resposta JSON, como também pode validar XML.
Vamos supor que façamos uma solicitação parahttp://localhost:8080/employees e recebamos a seguinte resposta:
Jane
Daisy
f
Podemos verificar quefirst-name éJane assim:
@Test
public void givenUrl_whenXmlResponseValueTestsEqual_thenCorrect() {
post("/employees").then().assertThat()
.body("employees.employee.first-name", equalTo("Jane"));
}
Também podemos verificar se todos os valores correspondem aos valores esperados, encadeando os correspondentes do corpo da seguinte maneira:
@Test
public void givenUrl_whenMultipleXmlValuesTestEqual_thenCorrect() {
post("/employees").then().assertThat()
.body("employees.employee.first-name", equalTo("Jane"))
.body("employees.employee.last-name", equalTo("Daisy"))
.body("employees.employee.sex", equalTo("f"));
}
Ou usando a versão abreviada com argumentos variáveis:
@Test
public void givenUrl_whenMultipleXmlValuesTestEqualInShortHand_thenCorrect() {
post("/employees")
.then().assertThat().body("employees.employee.first-name",
equalTo("Jane"),"employees.employee.last-name",
equalTo("Daisy"), "employees.employee.sex",
equalTo("f"));
}
10. XPath para XML
We can also verify our responses using XPath. Considere o exemplo abaixo que executa um matcher nofirst-name:
@Test
public void givenUrl_whenValidatesXmlUsingXpath_thenCorrect() {
post("/employees").then().assertThat().
body(hasXPath("/employees/employee/first-name", containsString("Ja")));
}
XPath também aceita uma maneira alternativa de executar o matcherequalTo:
@Test
public void givenUrl_whenValidatesXmlUsingXpath2_thenCorrect() {
post("/employees").then().assertThat()
.body(hasXPath("/employees/employee/first-name[text()='Jane']"));
}
11. Detalhes do teste de log
11.1. Detalhes de solicitação de registro
Primeiro, vamos ver comolog entire request details using*log().all()*:
@Test
public void whenLogRequest_thenOK() {
given().log().all()
.when().get("/users/eugenp")
.then().statusCode(200);
}
Isso registrará algo como isto:
Request method: GET
Request URI: https://api.github.com:443/users/eugenp
Proxy:
Request params:
Query params:
Form params:
Path params:
Multiparts:
Headers: Accept=*/*
Cookies:
Body:
Para registrar apenas partes específicas da solicitação, temos o métodolog() em combinação comparams(), body(), headers(), cookies(), method(), path(), por exemplolog.().params().
Observe que outras bibliotecas ou filtros usados podem alterar o que é realmente enviado ao servidor, então isso só deve ser usado para registrar a especificação da solicitação inicial.
11.2. Detalhes de resposta de log
Da mesma forma, podemos registrar os detalhes da resposta.
No exemplo a seguir, estamos registrando apenas o corpo da resposta:
@Test
public void whenLogResponse_thenOK() {
when().get("/repos/eugenp/tutorials")
.then().log().body().statusCode(200);
}
Saída de amostra:
{
"id": 9754983,
"name": "tutorials",
"full_name": "eugenp/tutorials",
"private": false,
"html_url": "https://github.com/eugenp/tutorials",
"description": "The \"REST With Spring\" Course: ",
"fork": false,
"size": 72371,
"license": {
"key": "mit",
"name": "MIT License",
"spdx_id": "MIT",
"url": "https://api.github.com/licenses/mit"
},
...
}
11.3. Registrar a resposta se a condição tiver ocorrido
Também temos a opção de registrar a resposta apenas se ocorrer um erro ou se o código de status corresponder a um determinado valor:
@Test
public void whenLogResponseIfErrorOccurred_thenSuccess() {
when().get("/users/eugenp")
.then().log().ifError();
when().get("/users/eugenp")
.then().log().ifStatusCodeIsEqualTo(500);
when().get("/users/eugenp")
.then().log().ifStatusCodeMatches(greaterThan(200));
}
11.4. Registrar se a validação falhar
Também podemos registrar solicitação e resposta somente se nossa validação falhar:
@Test
public void whenLogOnlyIfValidationFailed_thenSuccess() {
when().get("/users/eugenp")
.then().log().ifValidationFails().statusCode(200);
given().log().ifValidationFails()
.when().get("/users/eugenp")
.then().statusCode(200);
}
Neste exemplo, queremos validar que o código de status é 200. Somente se isso falhar, a solicitação e a resposta serão registradas.
12. Conclusão
Neste tutorial, temosexplored the REST-assured frameworke olhamos seus recursos mais importantes que podemos usar para testar nossos serviços RESTful e validar suas respostas.
A implementação completa de todos esses exemplos e trechos de código pode ser encontrada em REST-asseguradoGitHub project.