No meu artigo passado, demostrei a facilidade do uso da biblioteca Flurl no consumo de dados de uma API. Entretanto o ponto de maior orgulho desta biblioteca é a facilidade em testar as requisições criadas com ela.
Testes de unidades em requisições HTTP
Testar classes que fazem requisições HTTP pode ser um ponto complexo do projeto. Ele precisa ser desenvolvido de forma que aceite um Mock ou fazer uso de uma API de testes.
Uma API de testes pode ser uma boa escolha, mas caso ocorra algo com a mesma, os testes do projeto irão falhar. Enquanto o problema com a API não for resolvido, estes testes não poderão ser realizados.
Então, o mais comum é o uso de Mock, que deve ser implementado de acordo com a biblioteca de requisições implementada, já que a classe HttpClient
não pode ser “mockada”. Ela não define interfaces que facilitariam este processo.
Dentre as bibliotecas de requisição que permitem o uso de Mock, a Flurl é a que torna este processo o mais transparente e simples.
Definindo testes de unidade com Flurl
Para exemplificar, serão implementados testes de unidade no projeto apresentado no meu artigo anterior. Nele vimos a implementação do Flurl na camada repository do projeto, assim, os testes de unidade mostrados aqui serão realizados apenas nesta camada.
Os testes podem ser feito graças a classe HttpTest
da biblioteca. Recomenda-se que ela seja declarada em um bloco using
:
using (var httpTest = new HttpTest()) {
// Realizar as requisições aqui
}
Desta forma, todas as requisições realizadas dentro deste bloco serão interceptadas pela classe e com isso, poderão ser “mockadas”:
public async Task TestListProducts()
{
var repository = new ProductRepository();
using (var httpTest = new HttpTest()) {
// arrange
httpTest.RespondWith("[{\"id\":\"12d3d23\",\"name\":\"Mouse\", \"quantity\":10, \"price\": 99.9}, {\"id\":\"213drwa3\",\"name\":\"Teclado\", \"quantity\":20, \"price\": 149.9}]");
// act
await repository.FindAll();
// assert
httpTest
.ShouldHaveCalled("http://localhost:3002/api/products")
.WithVerb(HttpMethod.Get);
}
}
Acima, é definido o mock:
httpTest.RespondWith("[{\"id\":\"12d3d23\",\"name\":\"Mouse\", \"quantity\":10, \"price\": 99.9}, {\"id\":\"213drwa3\",\"name\":\"Teclado\", \"quantity\":20, \"price\": 149.9}]");
Ou seja, não importa qual requisição, será retornado este conteúdo.
Em seguida a requisição é realizada:
await repository.FindAll();
E é verificado se ela foi feita para a API via GET:
httpTest
.ShouldHaveCalled("http://localhost:3002/api/products")
.WithVerb(HttpMethod.Get);
Ao executar o teste, ele irá passar:
Definindo critérios para as requisições interceptadas
No exemplo anterior todas as requisições realizadas serão interceptadas pela classe HttpTest
. Entretanto, é possível refinar isso:
public async Task TestCreateProducts()
{
var repository = new ProductRepository();
using (var httpTest = new HttpTest()) {
// arrange
httpTest
.ForCallsTo("http://localhost:3002/*", "https://api.com/*")
.WithVerb(HttpMethod.Post)
.RespondWith("[{\"id\":\"12d3d23\",\"name\":\"Mouse\", \"quantity\":10, \"price\": 99.9}", 201);
var product = new Product {
Name = "Mouse",
Quantity = 10,
Price = 99.9
};
// act
await repository.Add(product);
// assert
httpTest
.ShouldHaveCalled("*/api/products")
.WithRequestBody("{\"Id\":*,\"Name\":\"Mouse\",\"Quantity\":10,\"Price\":99.9}")
.WithVerb(HttpMethod.Post);
}
}
Note que é definido as APIs que serão interceptadas e o verbo HTTP:
httpTest
.ForCallsTo("*localhost:3002/*", "*api.*.com/*")
.WithVerb(HttpMethod.Post)
Qualquer requisição que não se enquadre nestes critérios será ignorada pela HttpTest
.
No assert, se verifica apenas o endpoint:
httpTest
.ShouldHaveCalled("*/api/products")
Não é necessário informar o servidor, porque os aceitos já estão definidos no critério especificado. Também note que é utilizado curingas (*
). Eles podem ser implementados nos parâmetros de todos os métodos da classe.
Caso o teste seja executado, este segundo também passará:
Conclusão
Adicionar testes em um projeto é uma boa prática que todos devem adotar. Ao se trabalhar com requisições, este processo pode ser facilitado ao adotar a biblioteca Flurl. Assim, caso esteja trabalhando com requisições e necessite implementar testes, não deixe de verificar esta biblioteca.
Neste artigo não foram abordados todos os métodos da classe HttpTest
. Como vários são úteis, não deixe de vê-los na documentação da mesma.
Então é isso, por hoje é só :)