DEV Community

Arthur Fücher for High5Devs

Posted on • Originally published at high5devs.com on

FsCheck: Mudando sua visão sobre Testes

Existem momentos em um projeto que simplesmente testar os valores absolutos de entrada e saída de um método não são suficientes para eu me sentir seguro. Um exemplo simples é uma função de multiplicação, acho que nunca vou me sentir seguro de saber que pensei em todas entradas e saídas. Nestes casos uma opção é a criação de testes de propriedade, e para C# existe a biblioteca FsCheck.

A ideia desse post é apresentar a biblioteca FSCheck e como ela me ajudou a fazer um teste num projeto que estou participando.


FSCheck

O projeto consiste em mostrar uma pergunta e a pessoa responde, se a pessoa erra a dificuldade da pergunta aumenta e se a pessoa acerta a dificuldade diminui. Isso influencia para saber a periodicidade que devo mostrar a pergunta para a pessoa.

Esse post não tem a intenção de explicar conceitos básicos de teste de propriedade ou testes generativos

Se acharem interessante fazer um post sobre isso, mandem nos comentários 🙂

Quem faz o cálculo dessa dificuldade nova, são uns algoritmos bem legais (mas que eu não criei). Existem vários, e na real, não me importa muito o que eles fazem. Porém, eu quero ter certeza de que se uma pergunta tinha um nível de dificuldade e a pessoa acertou, o novo nível de dificuldade deve ser menor que o anterior.

Resumindo, podemos ver que o meu método tem algumas propriedades:

  • Quando acertar, a dificuldade tem que diminuir;
  • Quando errar, a dificuldade tem que aumentar;

E é isso que quero testar, uma propriedade do meu método.

O código que tem a propriedade que quero testar é o método que é executado na primeira linha desse trecho de código:


public Cartão AtualizarDadosDeRevisão(ResultadoEnum resultado, IEstratégia estratégia)
{
  var novaDificuldade = estratégia.AjustarDificuldade(this, resultado);
  this.PorcentagemDificuldade = novaDificuldade;
  return this;
}

Enter fullscreen mode Exit fullscreen mode

Em um teste que estamos acostumados a fazer iríamos ter um código mais ou menos assim:


[Test]
public void DeveDiminuirDificuldadeQuandoAcertarAResposta()
{
  var estratégia = new SuperMemo2Estratégia();
  var cartão = new CartãoBuilder().ComPorcentagemDeDificuldade(100).Generate();

  var novaPorcentagem = estratégia.AjustarDificuldade(cartão, ResultadoEnum.Acertou);

  Assert.True(novaPorcentagem < cartão.PorcentagemDificuldade);

  //Temos que fazer esse Assert, pois como disse não sei (ou não me importo) com a implementação da estratégia.
  //Se tivesse certeza do resultado final, poderia fazer algo assim:
  //novaPorcentagem.Should().Be(96);
}

Enter fullscreen mode Exit fullscreen mode

Esse teste em um primeiro momento, testa o que preciso e é bem simples. Porém, se for pensar, será que para a entrada 99 ele funciona? E para 98? E para 97? E para.. Bom acho que você entendeu hehehe

Então como testar uma propriedade e tentar garantir um número maior de entradas?

FSCheck é uma biblioteca que provê formas para testar automaticamente código baseado em propriedades.

Como no projeto estamos utilizando NUnit para testes, precisamos adicionar uma versão específica: FsCheck.NUnit

Com o FsCheck mudamos o teste de um teste de unidade para um teste de propriedade, portanto as duas primeiras alterações são:

  • Alterar a annotation de Test para Property;
  • O retorno do método de teste agora é Property ao invés de void;

Com a mudança do retorno, devemos agora retornar qual a propriedade que queremos testar, que no nosso caso é se a novaPorcentagem é menor que a anterior. Com essas mudanças nosso teste está assim por enquanto:


[Property] 
public Property DeveDiminuirDificuldadeQuandoAcertarAResposta() 
{
 var estratégia = new SuperMemo2Estratégia();
 var cartão = new CartãoBuilder().ComPorcentagemDeDificuldade(100).Generate();
 var novaPorcentagem = estratégia.AjustarDificuldade(cartão, ResultadoEnum.Acertou);

 return (novaPorcentagem < cartão.PorcentagemDificuldade).ToProperty(); 
}

Enter fullscreen mode Exit fullscreen mode

Nesse teste, fica explícita a propriedade que queremos testar! Porém, continuamos com o valor fixo de 100, caindo no mesmo problema anterior. O FsCheck consegue cuidar disso para nós, para isso basta eu dizer para ele que quero que ele gere, expondo como um parâmetro do meu teste o que ele precisa gerar.

Portanto vamos alterar a assinatura do nosso teste para receber a porcentagem e usá-la no teste:


[Property] 
public Property DeveDiminuirDificuldadeQuandoAcertarAResposta(uint porcentagem) 
{
 var estratégia = new SuperMemo2Estratégia();
 var cartão = new CartãoBuilder().ComPorcentagemDeDificuldade(porcentagem).Generate();
 var novaPorcentagem = estratégia.AjustarDificuldade(cartão, ResultadoEnum.Acertou);

 return (novaPorcentagem < cartão.PorcentagemDificuldade).ToProperty(); 
}

Enter fullscreen mode Exit fullscreen mode

E ao rodar o teste temos a seguinte saída:

Executando AstroCards.test.AstroCards.UnitTests.AstroCards.UnitTests.Specs.EstrategiasRevisao.SuperMemo2EstratégiaTestes.DeveAumentarDificuldadeQuandoErrarAResposta ...
Ok, passed 100 tests.
Enter fullscreen mode Exit fullscreen mode

Isso pode gerar uma confusão, já que criamos apenas um teste. O que acontece é que o FsCheck por padrão gera 100 entradas para o nosso teste.

Se quiser ver as entradas, pode utilizar o método Collect:


return (novaPorcentagem < porcentagem).ToProperty().Collect(porcentagem);

Enter fullscreen mode Exit fullscreen mode

Ao rodar o teste teremos algo parecido com isso na saída:

Ok, passed 100 tests.
7% 3u.
5% 6u.
5% 17u.
4% 7u.
4% 28u.
3% 9u.
3% 8u.
3% 5u.
3% 4u.
3% 1u.
2% 64u.
2% 48u.
2% 47u.
2% 42u.
2% 40u.
2% 2u.
2% 29u.
etc...

Enter fullscreen mode Exit fullscreen mode

Ele mostra todas as entradas que foram testadas e, a esquerda delas, qual a % de vezes que aquela mesma entrada foi usada.

O mais interessante, isso tudo rodou em 1.246s! Com isso consigo testar diversas entradas, e garantir que a propriedade do meu método funciona!

Agora um ponto de atenção, só dissemos para o FsCheck que queremos uma entrada do tipo uint, porém para nossa porcentagem queremos somente números de 0 até 100.

O FsCheck disponibiliza formas de lidar com isso, mas isso é um assunto para um próximo post!


Eai, gostou? Tem dúvidas? Quer saber mais sobre o FsCheck ou Testes de Propriedade?

Deixe um comentário 🙂

Abraços

Imagem usada no post Chris Liverani on Unsplash

O post FsCheck: Mudando sua visão sobre Testes apareceu primeiro em High5Devs.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay