Testando RestClient com @RestClientTest
Depois de concluirmos este webinar, nós:
- Compreenderemos como podemos autoconfigurar o sistema sob teste com
@RestClientTestno Spring Boot 4. - Saberemos criar stubs e mocks de requisições HTTP utilizando a classe
MockRestServiceServer. - Saberemos como escrever testes para clientes REST (
RestClienteRestTemplate) com JUnit 6 e Kotlin.
Este webinar assume que:
- Você sabe especificar o contexto de aplicação carregado pelo Spring TestContext Framework.
- Você está familiarizado com o JUnit 6.
- Você sabe criar um projeto que executa testes unitários, de integração e End-to-End com
@Tag.
Introdução ao Sistema sob Teste
Durante este webinar, escreveremos testes para um serviço que gerencia objetos Message. Um objeto Message contém o id e o text de uma única mensagem. O código-fonte da data class Message em Kotlin é o seguinte:
|
|
A classe MessageService gerencia mensagens consumindo uma API REST remota. Ela possui duas dependências injetadas via construtor:
- Ela utiliza um
RestClient.Builder(ouRestTemplateBuilder) para construir o cliente HTTP que envia requisições para a API REST remota. - Ela utiliza um objeto
MessageApiUrlBuilderpara construir as URLs usadas na comunicação com a API REST.
Esta classe possui dois métodos:
- O método
create()cria uma nova mensagem enviando uma requisiçãoPOSTpara a API REST e retorna as informações da mensagem criada. - O método
findById()busca as informações da mensagem solicitada enviando uma requisiçãoGETpara a API REST. Se nenhuma mensagem for encontrada (HTTP 404), este método retornanull(ouOptional.empty()).
O código-fonte da classe MessageService em Kotlin utilizando a API fluente do RestClient do Spring Boot 4 (compatível com o MockRestServiceServer do @RestClientTest) é o seguinte:
|
|
A classe MessageApiUrlBuilder é responsável por construir as URLs que utilizamos ao nos comunicarmos com a API REST remota. Usar uma classe construtora de URLs permite ler a URL base a partir de um arquivo de propriedades (@Value("\${message.api.base.url}")) e usar diferentes URLs base em diferentes ambientes:
|
|
A seguir, descobriremos como podemos configurar o sistema sob teste utilizando a autoconfiguração do Spring Boot.
Configurando o Sistema sob Teste com @RestClientTest
Podemos configurar o sistema sob teste seguindo estes passos:
- Anotar a classe de teste com
@RestClientTeste especificar os componentes testados que devem ser adicionados ao contexto de aplicação carregado (MessageApiUrlBuilder::classeMessageService::class). - Configurar o perfil ativo do Spring (
@ActiveProfiles(Profiles.UNIT_TEST)). Isso nos permite carregar a URL base da API REST remota (http://localhost:9090) a partir de um arquivo de propriedades específico de teste. - Anotar a classe de teste com
@Tag(TestTags.UNIT_TEST). - Injetar o bean
MessageServicena nossa classe de teste. - Injetar o bean
MockRestServiceServerna nossa classe de teste. Usaremos este campo quando quisermos criar stubs e mocks de requisições HTTP enviadas para a API REST. - Criar um método
@BeforeEachque reseta o estado do servidor mock (server.reset()) antes da execução de cada método de teste. Precisamos fazer isso porque, caso contrário, não conseguiríamos especificar novas expectativas após enviar uma requisição HTTP ao servidor mock.
Após termos configurado o sistema sob teste, o código-fonte da nossa classe de teste fica assim:
|
|
Há dois pontos importantes que quero destacar:
- Por padrão, a anotação
@RestClientTestautoconfigura o suporte ao Jackson (ObjectMapper), além de configurar os beansRestClient.Builder,RestTemplateBuildereMockRestServiceServerconectados em memória (sem abrir sockets TCP reais).- Como você deve ter notado, adicionei esta classe de teste à nossa suíte de testes unitários (
TestTags.UNIT_TEST) porque ela não possui dependências de infraestrutura externa (nem banco de dados, nem servidor HTTP real). Faz todo sentido usar uma unidade ligeiramente maior aqui porque o@RestClientTestvalida a serialização JSON real do Jackson e a montagem das URLs em poucos milissegundos!
Vamos avançar e descobrir como podemos fazer stubbing de chamadas de API REST com o MockRestServiceServer.
Fazendo Stubbing de Chamadas de API REST (findById)
Quando queremos configurar a resposta retornada ao invocarmos a API REST remota, seguimos estas regras:
- Identificamos a requisição HTTP esperada invocando o método
expect()da classeMockRestServiceServer. Este método recebe um objetoRequestMatchercomo parâmetro e retorna um objetoResponseActions. - O objeto
RequestMatcherdetermina se nossa expectativa foi atendida. Criamos objetosRequestMatcherusando os métodos fábrica estáticos da classeMockRestRequestMatchers(comorequestTo(...),method(...),header(...),jsonPath(...)). - O objeto
ResponseActionspossui dois métodos principais: se quisermos especificar mais expectativas sobre a requisição, invocamosandExpect(); se quisermos configurar a resposta HTTP simulada, invocamosandRespond()passando umResponseCreator. - Criamos objetos
ResponseCreatorusando os métodos fábrica da classeMockRestResponseCreators(comowithStatus(...)ewithSuccess(...)).
Vamos colocar essas habilidades em prática e escrever os testes para o método findById() da classe MessageService:
Primeiro, criamos a classe interna @Nested chamada FindById.
Segundo, garantimos que o sistema sob teste funcione conforme o esperado quando a mensagem solicitada não é encontrada:
- Criar a classe interna
@NestedchamadaWhenMessageIsNotFound. - Garantir no
@BeforeEachque oMockRestServiceServerretorne o código de status HTTP 404 (withStatus(HttpStatus.NOT_FOUND)) quando tentarmos buscar a mensagem de ID99. - Verificar se o método
findById(99L)retornanull.
Terceiro, garantimos que o sistema sob teste funcione conforme o esperado quando a mensagem solicitada é encontrada:
- Criar a classe interna
@NestedchamadaWhenMessageIsFound. - Garantir no
@BeforeEachque oMockRestServiceServerretorne o JSON da mensagem solicitada com status 200 (withSuccess(..., MediaType.APPLICATION_JSON)). - Verificar se o sistema sob teste retorna um objeto
Messagecom oidcorreto (99L). - Garantir que o sistema sob teste retorne um objeto
Messagecom otextcorreto ("Hello World!").
Após termos escrito esses testes, o código-fonte da seção FindById fica assim:
|
|
Agora sabemos escrever testes para métodos que buscam informações de uma API REST remota. A seguir, descobriremos como podemos fazer mocking (verificação de interação) de chamadas de API REST usando a classe MockRestServiceServer.
Fazendo Mocking e Verificando Chamadas de API REST (create)
Quando especificamos nossas expectativas invocando o método expect() do MockRestServiceServer e queremos verificar explicitamente que a requisição HTTP esperada foi enviada X vezes com o método, cabeçalhos e corpo JSON corretos, seguimos estas regras:
- Podemos especificar a quantidade esperada de invocações passando um objeto
ExpectedCount(comoExpectedCount.times(1)ouExpectedCount.once()) como primeiro parâmetro do métodoexpect(). Se omitirmos esse parâmetro, oMockRestServiceServerassume que esperamos apenas uma requisição. - Invocamos o sistema sob teste e verificamos se a requisição HTTP esperada foi realmente enviada
Xvezes chamando o métodoserver.verify()da classeMockRestServiceServer.
Vamos ver como podemos garantir que o método create() da classe MessageService crie apenas uma mensagem enviando o payload JSON correto e retorne as informações da mensagem criada:
- Garantir no
@BeforeEachque oMockRestServiceServerespere receber exatamente uma requisiçãoPOSTparahttp://localhost:9090/api/messagecomContent-Type: application/json,$.idnulo e$.textigual a"Hello World!", respondendo com o JSON da mensagem criada. - Verificar se o sistema sob teste cria uma mensagem invocando a API remota uma vez (
server.verify()). - Garantir que o sistema sob teste retorne um
Messagecom oidcorreto (99L). - Verificar se o sistema sob teste retorna um
Messagecom o texto correto ("Hello World!").
Após termos escrito todos os testes, o código-fonte completo de MessageServiceTest fica assim:
|
|
Agora sabemos escrever testes para clientes REST com Spring Boot 4 e JUnit 6. Vamos resumir o que aprendemos neste webinar.
Resumo
Este webinar nos ensinou três coisas:
- Podemos autoconfigurar o sistema sob teste (incluindo Jackson,
RestClient.Builder/RestTemplateBuildereMockRestServiceServer) utilizando a anotação@RestClientTest. - Podemos fazer stub e mock de requisições HTTP síncronas utilizando a classe
MockRestServiceServer. - Devemos lembrar de resetar o estado do servidor mock (
server.reset()) no@BeforeEachantes que cada método de teste seja executado.