Jest: O poder oculto dos matchers

Elementos diários para melhorar seus testes

Image for post
Image for post
Descobrindo e entendendo elementos secretos no Jest!

Não são muitas pessoas que sabem, mas Jasmine fornece a capacidade de personalizar o resultado da verificação de igualdade que é utilizado internamente por toEqual, toHaveBeenCalledWith e outros matchers.

Isso significa que se o objeto do lado direito tiver um método asymmetricMatch, o resultado de sua invocação será usado ao invés da verificação de igualdade profunda (deep-equality check).

Esse recurso está disponível no Jasmine e descrito muito bem na documentação oficial. Jest, que usa o Jasmine sob o capô, também suporta oficialmente isso, como é descrito na documentação.

Hoje vou tentar destacar alguns exemplos, onde as correspondências assimétricas serão surpreendentemente poderosas e permitirão que você escreva um código muito mais simples do que antes. Todos os exemplos de código abaixo serão baseados no Jest, no entanto, a diferença com o Jasmine não será muito grande, a abordagem geral será a mesma.

1. Testar chamadas com funções como argumentos ☎️

Imagine que você precise testar se algum método de API foi chamado corretamente. Quando todos os parâmetros do método são simples ou primitivos, é fácil:

Mas se o método também aceita um retorno de chamada (callback), não será tão simples:

Isso não funcionará porque as funções não são iguais entre si. Para fazer isso funcionar, você precisa armazenar uma referência do callback e usá-lo posteriormente na declaração:

Mas isso nem sempre é possível. Em alguns casos, esse retorno de chamada será fornecido por outra pessoa e você não terá acesso direto a ela. Portanto, não poderá usar toHaveBeenCalledWith para este tipo de testes.

Este é o caso, onde as correspondências assimétricas podem ser úteis. Podemos escrever um teste que verifique que esse método foi chamado com alguma função, não importando qual exatamente. Jest (na verdade, Jasmine) nos dá um conjunto de correspondências assimétricas predefinidas, por exemplo expect.any(<type>), que retorna true para qualquer valor com o tipo especificado:

Ótimo! Agora podemos fazer testes que pulem alguns valores, que não precisamos verificar estritamente. Além disso, podemos usar expect.anything para dizer que aqui qualquer valor é permitido, exceto undefined ou null. Isso pode ser útil, quando apenas alguns argumentos do método fazem sentido para você:

Também podemos usar essas correspondências em qualquer nível, não apenas na raíz, para verificar tipos de argumentos fornecidos, sem mencionar valores:

Usar correspondências assimétricas como um espaço reservado para alguns argumentos de espião ajuda você a se concentrar nas coisas mais importantes em seus testes.

2. Ignorando valores, em relação ao horário atual 🕐

Podemos usar a mesma técnica para verificações de alguns valores baseados em seu ambiente de teste, por exemplo, dados produzidos a partir da data atual.

Toda vez que, ao escrever uma declaração no objeto, que contém timestamp gerado, você precisa encontrar uma maneira de simular a hora do sistema. Você pode usar qualquer biblioteca de simulação de tempo para gerar Date.now() ou new Date() determinístico, mas também pode usar a magia de correspondência assimétrica. Por exemplo, temos uma função, que gera um objeto representando uma entidade de comentário, que será salva. Tem um campo createdAt que é baseado na hora atual de que foi gerado. Ao escrever um teste, podemos colocar um espaço assimétrico reservado, ao invés de timestamp:

Agora, temos tudo testado, mas não esbarramos muito em pequenos detalhes. Basicamente, o padrão é o seguinte: se você tiver algum valor, que deve existir nos dados testados, mas não sabe exatamente o que é, substitua-o por expect.any ou expect.anything.

3. Correspondência parcial para objetos complexos 📬

Vamos ver outros casos de uso, por exemplo, quando você está recebendo um objeto grande, mas apenas alguns campos fazem sentido para você. Quando você está testando esse código, você também gostaria de mencionar apenas os campos importantes, mas não outros, que podem ser alterados, sem qualquer efeito para o seu código. Com correspondência assimétrica, você pode usar expect.objectContaining e expect.arrayContaining para te ajudar:

Você pode deixar seu código de teste mais limpo, verificando um conjunto limitado de valores, que é importante para o teste. Lembre-se do padrão crucial para testes: um teste — um recurso.

O mesmo funciona para Arrays. Se você está recebendo um array de TODOs, mas precisa encontrar apenas os adicionados recentemente, você pode usar expect.arrayContaning:

Aqui você não precisa se preocupar com a ordem dos itens. O objetivo do teste era verificar se o todo adicionado recentemente aparece na lista, e fizemos isso muito bem.

Colocando tudo junto 🛍

Aqui, tentei destacar alguns casos de uso, em que correspondências assimétricas podem ser úteis. Mas esta não é a lista completa. Lembre-se de que, você pode substituir qualquer valor complexo por um simples espaço reservado para tornar seu código de teste mais curtos e expressivos.

Então, eu gostaria de terminar a postagem com este pequeno trecho de código, onde você pode ver todas as correspondências assimétricas juntas:

Vamos assumir que store.getState() produz um objeto muito grande. Normalmente, você tem que escrever várias afirmações para verificar esses dados. Mas com matchers assimétricos você pode fazer isso com uma simples verificação.

Verifique tudo que é importante para você e ignore o resto.

Espero que você também tenha achado esses recursos úteis, experimente em seus projetos e compartilhe o que você achou!

Happy coding! 🎉❤️

⭐️ Créditos

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store