Mas eai, me mostra exemplos!? #WIP

Claro, mostro sim, segue esse artigo que você vai entender como testar alguns cenários, e alguns dos pensamentos que temos quando estamos escrevendo testes.

📑

Organização de um teste

Test assertion concept

Vamos usar o modelo Teste Pilha Palito, não - calma… brincadeira, é que temos um modelo de organização que abreviado fica AAA: Arrange > Act > Assert.

Ou seja, dentro do teste temos:

  • Arrange (Preparação): mocks, spys, renders, e outras coisas necessárias para que o contexto seja válido;
  • Act (Ação): aqui nós disparamos alguma ação, seja um render de componente, ou um clique de botão, ou preenchimento de input, chamada de função, etc.
  • Assert (Verificação/Comparação): após a ação, nós pegamos o resultado e comparamos se está tudo ok;

O teste roda em cascata, executando de cima para baixo, então se seu Assert verifica um clique em um botão para uma determinada ação, você precisa executar essa ação antes, no Act. E se o seu Act precisa de um contexto ou uma view renderizada, você executa isso no Arrange.

Alguns testes de tela possuem apenas duas etapas, onde o Arrange e Act são o mesmo, como um render(), por exemplo, ou seja, nem todos os testes seguem rigorosamente esse modelo, nem devem, na verdade, essa é uma forma de organizar o teste para que fique mais simples, você deve usar para ser guiado durante o desenvolvimento.

Testando o comportamento de um componente

Vamos considerar o formulário abaixo como exemplo:

form.png

Referência da imagem: https://tailwindcomponents.com/component/minimal-login-form-2

Nos exemplos a seguir, estaremos escrevendo os testes utilizando a Vue Testing Library.

Componente renderizado corretamente

Existem vários cenários de teste que podem ser aplicados nesse componente, vamos começar verificando se os campos foram renderizados corretamente para o usuário.

test("Should render the component properly", () => {
    // act
    render(Component);

    // assert
    expect(screen.getByRole("textbox", { name: /First name/i })).toBeInTheDocument();
    expect(
      screen.getByRole("textbox", { name: /Last name/i })
    ).toBeInTheDocument();
    expect(screen.getByRole("textbox", { name: /Company/i })).toBeInTheDocument();
    expect(screen.getByRole("textbox", { name: /E-mail/i })).toBeInTheDocument();
    expect(screen.getByRole("button", { name: /Login/i })).toBeDisabled();
  });

Seguindo o princípio que devemos escrever testes que assemelham como a página é utilizada pelo usuário, foi utilizada a query getByRole para obter os elementos conforme a sua função. Par esse tipo de teste, podemos verificar se os elementos críticos para funcionamento do componente foram exibidos, o estado inicial de alguns componentes conforme alguma regra de negócio (por exemplo, o botão deve estar desabilitado até o preenchimento do formulário), ou seja.

Nesse teste, usamos a biblioteca @testing-library/jest-dom para ter acessos a métodos de verificação no jest

Preenchimento de entrada de dados e emissão de eventos

Para o próximo testes, vamos validar se quando o formulário é preenchido corretamente o evento submit é emitido corretamente com as propriedades.

test("Should emit submit when valid data is inseted in the form", async () => {
    // arrange
    const user = userEvent.setup();
    const { emitted } = render(Component);

    //act
    await user.type(screen.getByRole("textbox", { name: /First name/i }, "Iron"));
    await user.type(
      screen.getByRole("textbox", { name: /Last name/i }, "Maiden")
    );
    await user.type(
      screen.getByRole("textbox", { name: /E-mail/i }, "iron@maiden.com")
    );
    await user.type(
      screen.getByRole("textbox", { name: /Company/i }, "Beast Inc.")
    );
    await user.click(screen.getByRole("button", { name: /Login/i }));

    //assert
    expect(emitted().submit).toBeTruthy();
    expect(emitted().submit[0]).toEqual([
      { firstName: "Iron", lastName: "Maiden", email: "iron@maiden.com", company: "Beast Inc." },
    ]);
  });

Na preparação do teste, antes do componente ser renderizado invocamos userEvent.setup(). A user-event é uma biblioteca complementar, a Testing Library simula a interação do usuário como se ele estivesse acontecendo no navegador. A seguir renderizamos o componente e obtemos o objeto emitted. Esse objeto irá conter os eventos emitidos conforme interagimos com os componentes.

Após a configuração do contexto, podemos usar o objeto user para preencher o formulário através do método user.type() e clicar no botão usando o user.click().

Na etapa de verificação, usamos o `emitted` para validar que o evento foi gerado corretamente.

Testando caminho infeliz

Em alguns componentes pode ser interessante validar o comportamento se algo errado acontecer.

test("Should not emit submit and display error message when email is invalid", async () => {
    // arrange
    const user = userEvent.setup();
    const { emitted } = render(Subscriber);

    // act
    await user.type(screen.getByRole("textbox", { name: /nome/i }, "Alice"));
    await user.type(
      screen.getByRole("textbox", { name: /celular/i }, "11988887777")
    );
    await user.type(screen.getByRole("textbox", { name: /email/i }, "alice"));
    await user.click(screen.getByRole("button", { name: /cadastrar/i }));

    //assert
    expect(emitted().submit).not.toBeTruthy();
    expect(screen.getByText(/informe um e-mail válido/i)).toBeInTheDocument();
  });

Componentes com propriedades

Em diversos componentes vamos precisar de passar propriedades externas. Para isso, o método render da Testing Library pode receber uma propriedade chamada props no objeto de opções:

<MyComponent message="hello" />

const props = { message: "hello" };
render(MyComponent, { props });

Componente com uso de slots

Semelhante ao exemplo anterior, podemos também podemos passar os slots no objeto de opções:

<template>
  <div>
    <slot name="description" />

    <slot name="content" />
  </div>
</template>



const slots = {
    description: '<p>Description text</p>',
    content: '<p>Content text</p>'
}

render(MyComponent, {slots});

expect(screen.getByText('Description text')).toBeInTheDocument()
expect(screen.getByText('Description text')).toBeInTheDocument()

Simulando funções

Quando precisamos validar comportamentos em funções chamadas pelos nossos componentes podemos utilizar alguns recursos de mock e spy do jest. Como exemplo, imagine que em um componente ao clicar em no botão devemos rolar para o início da tela utilizando o window.scrollTo(0, 0).

describe("My component", () => {
  const scrollToSpy = jest.fn();
  window.scrollTo = scrollToSpy;

  beforeEach(() => {
    jest.clearAllMocks();
  });

  test("Should scroll to top", async () => {    
    const user = userEvent.setup();
    render(MyComponent);

    await user.click(screen.getByRole("button", { name: /ir para início/i }));

    expect(scrollToSpy).toHaveBeenCalledTimes(1);
  });
});
Nertec logo