O que estou fazendo agora
Atualmente, estou testando uma implementação de código. Esses testes são interrompidos todas as vezes após a refatoração, especialmente nos componentes da interface do usuário. Como resultado, passo muito tempo pesquisando nos arquivos .test.js
, seguindo simultaneamente a figura mágica de 80% para a Cobertura de teste .
O que devo fazer
Ao escrever qualquer tipo de teste, incluindo teste de unidade, devo pensar menos no código que estou testando e mais no que esse código faz. Isso significa escrever testes que imitam o comportamento do usuário. Mesmo no nível mais baixo.
Exemplo
Imagine um componente padrão de acordeão yuy. Quando pressionado, ele abre ou fecha. O conteúdo é passado para o componente como children
.

A funcionalidade do nosso componente de teste é a seguinte:
- Os três primeiros acordeões são implantados, todos os demais estão fechados.
- Quando você clica no acordeão, o módulo
publishAccordionAnalytics
é publishAccordionAnalytics
, que rastreia a análise. Importamos este módulo do pacote @myProject/analyticsHelpers
- Se o usuário clicar no acordeão oculto na parte inferior do aplicativo e depois de abrir o conteúdo do acordeão não estiver no campo de visão do usuário, a animação será acionada e o conteúdo do componente será movido para a parte visível da tela.
... import { publishAccordeonAnalytics } from '@myProject/analyticsHelpers'; class Accordion extends Component { positionReference: RefObjectType; constructor(props) { super(props); this.positionReference = React.createRef(); } scrollToRef = (ref) => { const wrapper = document.querySelector('.app'); return isInViewPort(ref, wrapper) ? null : setTimeout(() => { return ref.current && wrapper && wrapper.scrollTo(wrapper.scrollTop, wrapper.scrollTop + ref.current.offsetTop) }, 300); }; render() { const { headerName, children, index } = this.props; const { scrollToRef, positionReference } = this; const defaultOpenAccordions = index >= 3; return ( <Accordion defaultOpen={!defaultOpenAccordions} headerName={headerName} onChange={(open) => { publishAccordionAnalytics(open, headerName); if (defaultOpenAccordions) { scrollToRef(positionReference); } }} id={headerName} > {children} {defaultOpenAccordions && <div data-testId="referenced-div" ref={positionReference} />} </Accordion> ); } }
Como vou escrever um teste agora:
jest.mock('@myProject/analyticsHelpers'); describe('', () => { test(' ', () => { const jsx = ( <Accordion headerName=" "> <div>Test</div> </Accordion> ); const tree = renderer .create(jsx) .toJSON(); expect(tree).toMatchSnapshot(); }); test(' ', () => { const wrapper = mount( <Accordion headerName=" " index={1}> <div>Test</div> </Accordion> ); wrapper.find('Header').simulate('click'); expect(publishAccordeonAnalytics).toHaveBeenCalledTimes(1); }); test(' scrollToRef ', () => { const wrapper = shallow( <Accordion headerName=" " index={7}> <div>Test</div> </Accordion>); const component = wrapper.instance(); component.scrollToRef = jest.fn(); wrapper.find('Header').simulate('click'); expect(publishAccordeonAnalytics).toHaveBeenCalledTimes(1); expect(component.scrollToRef).toHaveBeenCalled(); }) });
Testo a estrutura do componente usando instantâneos, bem como chamadas de função ao clicar.
Este acordeão atende totalmente à funcionalidade esperada e os testes confirmam isso. Mas agora vou fazer a refatoração, substituindo React.Component
pelo functional component
, e scrollToRef
componente scrollToRef
em uma função separada.
function Accordion ({ marketName, children, index }) { const positionReference = React.createRef(); const defaultOpenAccordions = index >= config.defaultOpenAccordions; return ( <Accordion defaultOpen={!defaultOpenAccordions} headerName={headerName} onChange={(open) => { publishAccordeonAnalytics(open, marketName); if (defaultOpenAccordions) { scrollToRef(positionReference); } }} id={marketName} > {children} {defaultOpenAccordions && <div data-testId="referenced-div" ref={positionReference} />} </Accordion> ); }
Meus testes caíram ... o teste ' scrollToRef '
falha porque A função scrollToRef
não scrollToRef
mais um método particular de componente. O mesmo aconteceria com o teste ' '
, mas é uma importação do módulo, então agora é aprovado.
Para escrever um bom teste, preciso entender como o usuário usa meu componente. Usuário:
- Encontrou um acordeão que contém as informações de que ele precisa
- Clicou no nome do acordeão para abri-lo
- Eu li ifna
- Acordeão fechado

Percebi que ele não se importa com o que meu componente é chamado, com o que ele clica e assim por diante. Seguindo esse princípio, eu deveria escrever algo assim:
import '@testing-library/jest-dom/extend-expect'; import { render, fireEvent, screen } from '@testing-library/react'; jest.mock('@myProject/analyticsHelpers'); describe('', () => { test(' ', () => { const child = <div></div>;
Agora reproduzi o comportamento do usuário. Encontrei o 10º acordeão, cliquei nele, li o conteúdo, fechei - é isso!
Este teste é visualmente muito mais limpo, pode suportar refatoração e simula a interação do usuário. E foi muito mais fácil para mim escrevê-las.
Usando esse padrão, podemos evitar o uso da instance()
Enzyme nativa instance()
, state()
, find('ComponentName')
e outras funções que testam a implementação do código.
Para o novo teste, eu usei