Mocky não morde! Dominando a simulação com a biblioteca de testes React

A tradução do artigo foi preparada em antecipação ao início do curso "Automação de Testes em JavaScript" .










Zombarias - não morda!



Eles são projetados para ajudá-lo a criar testes mais simples e confiáveis. Nesta série de artigos, vou mostrar os padrões nos quais confio ao simular (ou “esboçar”) componentes React.



Aqui está um bom exemplo de um esboço de componente. Eu uso jest.mock, que veremos com mais detalhes abaixo.



jest.mock("../src/PostContent", () => ({
  PostContent: jest.fn(() => (
    <div data-testid="PostContent" />
  ))
}))




Um esboço de componente React típico não deve parecer mais complicado. Observe o valor stub muito simples ( div) e um atributo data-testidque facilita muito a localização da instância renderizada no DOM. Por convenção, o identificador de teste usado deve corresponder ao nome do componente. Nesse caso, é PostContent.



Antes de vermos como eles são usados, vamos primeiro dar uma olhada no que são os mocks e por que você pode querer usá-los.



O que é uma simulação?



No mundo do JavaScript, o termo simulação é amplamente usado para se referir a qualquer implementação simulada ou teste duplo . Implementações simuladas são simplesmente valores que substituem outros em seu código de produção enquanto os testes estão sendo executados. Eles tentam a interface do objeto que está sendo substituído, de forma que o resto do código funcione como se não houvesse substituição.



Existem vários motivos pelos quais você pode querer fazer isso; vamos olhar para eles com exemplos.



Se você estiver interessado em aprender mais sobre implementações simuladas, leia o livro de Martin Fowler Mocks Are Not Stubs .



Brincadeira e zombaria



O Jest tem um recurso jest.mockque permite simular módulos inteiros que você substitui. Neste tutorial, concentrei-me neste recurso, embora existam outras maneiras de substituir objetos em JavaScript.



No livro Mastering React Test-Driven Development, eu uso importações de módulo nomeado ES6 para criar duplas de teste. Essa abordagem oferece um pouco mais de flexibilidade, mas parece um pouco mais hackeada.



Jest jest.mockele diz que as simulações garantem que seus testes sejam rápidos e não quebradiços .



Embora isso seja verdade, essa não é a principal razão de eu usar mocks.

Eu uso mocks porque eles me ajudam a manter meus testes independentes uns dos outros.



Para entender por que esse é o caso, vejamos um exemplo.



Por que moki?



Abaixo está uma lista de um componente BlogPageque faz duas coisas: busca iduma propriedade urle exibe um PostContentcomponente com ela id.



const getPostIdFromUrl = url =>
  url.substr(url.lastIndexOf("/") + 1)

export const BlogPage = ({ url }) => {

  const id = getPostIdFromUrl(url)

  return (
    <PostContent id={id} />
  )
}




Imagine que você está escrevendo testes para este componente e todos os seus testes estão incluídos em BlogPage.test.js, que é um único conjunto de testes que cobre os componentes BlogPagee PostContent.



Neste ponto, você não precisa dos mocks ainda: não vimos PostContentainda, mas dado o tamanho BlogPage, realmente não há necessidade de ter dois conjuntos de teste separados, já BlogPageque geralmente é simples PostContent.



Agora imagine adicionar funcionalidade a BlogPagey e y PostContent. Como um desenvolvedor talentoso, você adiciona mais e mais recursos todos os dias.



Torna-se mais difícil manter os testes em funcionamento. Cada novo teste tem uma configuração mais complexa e o conjunto de testes começa a consumir mais do seu tempo - uma carga que agora precisa ser suportada.

Este é um problema comum e eu vejo isso o tempo todo nas bases de código do React. Suítes de teste em que mesmo a alteração mais simples resultará em muitos testes falhando.



Uma solução é dividir os conjuntos de testes. Vamos sair BlogPage.test.jse criar um novo PostContent.test.jsque deve conter testes exclusivamente para PostContent. A ideia básica é que todas as funções colocadas em PostContentdevem estar em PostContent.test.jse todas as funções colocadas em BlogPage(como análise de URL) devem estar em BlogPage.test.js.



OK.



Mas e se renderizarPostContent tem efeitos colaterais?



export const PostContent = ({ id }) => {
  const [ text, setText ] = useState("")

  useEffect(() => {
    fetchPostContent(id)
  }, [id])

  const fetchPostContent = async () => {
    const result = await fetch(`/post?id=${id}`)
    if (result.ok) {
      setText(await result.text())
    }
  }

  return <p>{text}</p>
};




O conjunto de testes BlogPage.test.jsdeve estar ciente dos efeitos colaterais e estar preparado para lidar com eles. Por exemplo, ele terá que manter uma fetchresposta pronta .



A dependência que tentamos evitar dividindo nossos conjuntos de testes ainda existe.



A organização de nossos testes certamente melhorou, mas no final, nada aconteceu para tornar nossos testes menos frágeis.

Para isso, precisamos de um esboço (ou simulação) PostContent.

E na próxima parte veremos como fazer isso.



É realmente necessário?



A propósito, algumas palavras sobre como passar de testes de ponta a ponta para testes de unidade.



Ter duplas de teste é um indicador importante de que você está escrevendo testes de unidade.



Muitos testadores experientes iniciam novos projetos imediatamente com testes de unidade (e simulações) porque sabem que, à medida que sua base de código cresce, eles enfrentarão o problema de instabilidade de teste.

Os testes de unidade geralmente são muito menores do que os testes de ponta a ponta. Eles podem ser tão pequenos que geralmente não ocupam mais do que três ou quatro linhas de código. Isso os torna excelentes candidatos para práticas de codificação social, como programação em pares e conjuntos.


Mesmo quando fazemos testes de unidade, os testes duplos nem sempre são necessários - eles são apenas mais uma ferramenta em seu pacote que você precisa saber quando e como aplicar.



Na próxima parte, cobriremos as técnicas básicas de mocking .






All Articles