Em testes de software, frequentemente nos encontramos na situação de querer comparar o valor atual com o valor esperado.
É nesse momento que os matchers aparecem. Um matcher é basicamente um objeto que segue uma interface específica para realizar essa comparação.
Eles só possuem uma finalidade, criar testes mais descritivos, legíveis e robustos. Isso nos permite expressar as condições que esperamos de uma forma muito mais clara e fluída.
Por exemplo, em vez de usar um expect para verificar se uma variável é igual a 5, poderíamos utilizar um matcher para verificar se a variável “é igual a 5”, “é maior que 2” ou “é um número inteiro”.
Pô, Caique, puta trabalho só para comparar dois valores. Isso ai vai atrasar minha entrega
Calma lá! A gente só gasta horas em uma tarefa quando nao compreendemos como realiza-la. Para evitar que testes se tornem isso, vamos nos aprofundar um pouco mais nesse tipo de objeto.
Agora que sabemos o que é um matcher, vamos descobrir como eles funcionam. Para isso, vamos dar uma olhada no código dessa classe:
Essa classe descreve um contrato que devera ser seguido pelas suas especializações. Os principais métodos são :
- matches: É o método responsável por conter a lógica de um match. Ele recebe um item para ser comparado e um mapState. O seu retorno é um boleano que indica se o item satisfaz todas as condições impostas pelo Matcher.
- describe: Responsável por gerar a descrição do que o Matcher está realizando o match.
- describeMismatch: Responsável por dar mais contexto do mismatch que ocorreu. Esse método é invocado quando o método matches retorna falso.
Entendo um pouco mais sobre a função matches e describeMismatch
Um dos parâmetros que a função matches recebe é um Map. Ele tem como finalidade enriquecer as informações que o matches irá passar para o describeMismatch.
Quando utilizar o describeMismatch
- Validações complexas: Se o Matcher é responsável por realizar multiplas validações é interessante indicar a razão do teste ter falhado.
- Dado adicionais: As vezes o código sendo validado depende de algum dado computado. Podemos indicar isso através do mapState. Visualizando o processo
Agora que sabemos o que cada função faz, seria interessante entendermos como elas interagem entre si. Para isso, iremos desenhar um diagrama de sequência com mermaid:
sequenceDiagram
participant Teste
participant FrameworkDeTeste
participant MatcherPersonalizado
participant Descrição
Teste ->> FrameworkDeTeste: expect(valorAtual, matcher)
FrameworkDeTeste ->> MatcherPersonalizado: matches(item, estadoDaCorrespondência)
alt correspondência é false
MatcherPersonalizado ->> Descrição: describe(descrição)
Descrição -->> MatcherPersonalizado: textoDaDescrição
MatcherPersonalizado ->> Descrição: add(textoDaDescrição)
Descrição -->> FrameworkDeTeste: textoDoErro
FrameworkDeTeste -->> Teste: Lança erro de asserção com textoDoErro
else correspondência é true
FrameworkDeTeste -->> Teste: Teste passa
end
Depois de tudo isso, bora lá para a nossa pratica?
Colocando o Matcher para trabalhar
Finalmente a teoria acabou e chegou a hora da pratica. Bora entender como criar um Matcher para facilitar nossa vida?
O cenário
Imagine que voce esta desenvolvendo uma tela em Flutter e precisa testar um widget que recebe um e-mail como entrada. Para facilitar a nossa vida poderíamos
E agora?
No nosso segundo artigo de teste, vimos o funcionamento de um Matcher. A partir dai, reconhecemos ele como uma ferramenta que pode transformar a maneira como escrevemos e lemos nossos testes.
No exemplo pratico, demonstramos como podemos reduzir a duplicidade de código em testes através do emprego de Matchers, otimizando o processo e tornando tudo mais claro.
Espero que este artigo tenha ajudado no entendimento dos Matchers. Nos proximos artigos, vamos entender como funciona o processo de um teste de widget, e veremos como os Matchers podem nos ajudar nesse contexto.
Até a próxima pessoal!