Promoção no ar
Tempo restante da oferta:
09 Horas
57 Min
26 Seg
Assinar →

C# - Testando requisições com Flurl

Implemente testes unitários em requisições HTTP com o auxílio de Flurl.

Wladimilson M. Nascimento 6 anos atrás

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:

asciicast

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á:

asciicast

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ó :)

Continue lendo

Artigos relacionados

Um ano de acesso total para você viver de programação.

Todos os cursos e formações da TreinaWeb numa única assinatura, com professores que atuam no mercado, suporte direto e certificados. Do primeiro console.log ao mercado de trabalho.

  • 1 ano de acesso a todos os cursos
  • Cursos novos toda semana
  • Suporte direto com professores
  • Plano de carreira personalizado
  • Certificados digitais inclusos
  • Aplicativos para Android e iOS, com acesso offline
Garantia incondicional de satisfação

Teste por 7 dias e, se não fizer sentido para você, devolvemos seu dinheiro.

Acesso completo a todos os cursos e dezenas de Formações.

12 meses
Economize R$ 840,00 54% OFF

de 12x R$ 129,00 por apenas

12x R$ 59,00

sem juros no cartão de crédito ou R$ 599,00 à vista

Oferta termina em 09h 57min 26s

  • Acesso imediato e ilimitado durante 1 ano.
  • Plano de carreira personalizado.
  • Suporte direto com professores.
  • E muito mais!
Matricule-se agora