JavaScript Avançado

Boas Práticas de Testes em React: Testing Library, MSW e Estratégias de Mock de API para Times Ágeis

7 min de leitura

Boas Práticas de Testes em React: Testing Library, MSW e Estratégias de Mock de API para Times Ágeis

Fundamentos de Testes em React com Testing Library A Testing Library é uma biblioteca que enfatiza testes centrados no usuário, não na implementação. Ao contrário do Enzyme, que permite acessar estados internos, a Testing Library força você a testar como um usuário realmente interage com sua aplicação. Isso significa buscar elementos pelo texto visível, labels, placeholders e roles ARIA — nunca por seletores de classe ou ID diretos. O primeiro passo é entender a hierarquia de queries. Existem três categorias: (lança erro se não encontrar), (retorna null) e (assíncrono, ideal para elementos que aparecem após renderização). Para começar, instale as dependências: Aqui está um exemplo funcional de um teste básico: Mock de APIs com MSW (Mock Service Worker) MSW intercepta requisições HTTP no nível da rede, sem alterar seu código. Funciona tanto em testes quanto no desenvolvimento real. Configure handlers que definem como a rede deve responder: Crie um arquivo de configuração para seus handlers: Configure o servidor MSW no

<h2>Fundamentos de Testes em React com Testing Library</h2>

<p>A Testing Library é uma biblioteca que enfatiza testes centrados no usuário, não na implementação. Ao contrário do Enzyme, que permite acessar estados internos, a Testing Library força você a testar como um usuário realmente interage com sua aplicação. Isso significa buscar elementos pelo texto visível, labels, placeholders e roles ARIA — nunca por seletores de classe ou ID diretos.</p>

<p>O primeiro passo é entender a hierarquia de queries. Existem três categorias: <code>getBy</code> (lança erro se não encontrar), <code>queryBy</code> (retorna null) e <code>findBy</code> (assíncrono, ideal para elementos que aparecem após renderização). Para começar, instale as dependências:</p>

<pre><code class="language-bash">npm install --save-dev @testing-library/react @testing-library/jest-dom vitest</code></pre>

<p>Aqui está um exemplo funcional de um teste básico:</p>

<pre><code class="language-javascript">import { render, screen } from &#039;@testing-library/react&#039;;

import userEvent from &#039;@testing-library/user-event&#039;;

import { LoginForm } from &#039;./LoginForm&#039;;

describe(&#039;LoginForm&#039;, () =&gt; {

it(&#039;deve exibir mensagem de sucesso ao enviar&#039;, async () =&gt; {

render(&lt;LoginForm /&gt;);

const emailInput = screen.getByLabelText(/email/i);

const submitButton = screen.getByRole(&#039;button&#039;, { name: /entrar/i });

await userEvent.type(emailInput, &#039;user@example.com&#039;);

await userEvent.click(submitButton);

const successMessage = await screen.findByText(/bem-vindo/i);

expect(successMessage).toBeInTheDocument();

});

});</code></pre>

<h2>Mock de APIs com MSW (Mock Service Worker)</h2>

<p>MSW intercepta requisições HTTP no nível da rede, sem alterar seu código. Funciona tanto em testes quanto no desenvolvimento real. Configure handlers que definem como a rede deve responder:</p>

<pre><code class="language-bash">npm install --save-dev msw</code></pre>

<p>Crie um arquivo de configuração para seus handlers:</p>

<pre><code class="language-javascript">// src/mocks/handlers.js

import { http, HttpResponse } from &#039;msw&#039;;

export const handlers = [

http.get(&#039;https://api.example.com/users/:id&#039;, ({ params }) =&gt; {

return HttpResponse.json({

id: params.id,

name: &#039;João Silva&#039;,

email: &#039;joao@example.com&#039;

});

}),

http.post(&#039;https://api.example.com/login&#039;, async ({ request }) =&gt; {

const body = await request.json();

if (body.password === &#039;correct&#039;) {

return HttpResponse.json({ token: &#039;abc123&#039; }, { status: 200 });

}

return HttpResponse.json({ error: &#039;Inválido&#039; }, { status: 401 });

})

];</code></pre>

<p>Configure o servidor MSW no seu setup de testes:</p>

<pre><code class="language-javascript">// src/mocks/server.js

import { setupServer } from &#039;msw/node&#039;;

import { handlers } from &#039;./handlers&#039;;

export const server = setupServer(...handlers);</code></pre>

<p>No arquivo de configuração do seu test runner (vitest.config.js):</p>

<pre><code class="language-javascript">import { defineConfig } from &#039;vitest/config&#039;;

export default defineConfig({

test: {

globals: true,

environment: &#039;jsdom&#039;,

setupFiles: &#039;./src/mocks/setupTests.js&#039;

}

});</code></pre>

<p>E em setupTests.js:</p>

<pre><code class="language-javascript">import { beforeAll, afterEach, afterAll } from &#039;vitest&#039;;

import { server } from &#039;./mocks/server&#039;;

beforeAll(() =&gt; server.listen());

afterEach(() =&gt; server.resetHandlers());

afterAll(() =&gt; server.close());</code></pre>

<h2>Estratégias Avançadas de Mock</h2>

<p>Existem situações onde você precisa sobrescrever handlers padrão ou mockar de forma granular. Use <code>server.use()</code> para injetar handlers específicos em testes individuais:</p>

<pre><code class="language-javascript">import { http, HttpResponse } from &#039;msw&#039;;

import { server } from &#039;./mocks/server&#039;;

describe(&#039;UserProfile&#039;, () =&gt; {

it(&#039;deve exibir erro quando API falha&#039;, async () =&gt; {

// Sobrescreve o handler padrão

server.use(

http.get(&#039;https://api.example.com/users/:id&#039;, () =&gt; {

return HttpResponse.json(

{ error: &#039;Não encontrado&#039; },

{ status: 404 }

);

})

);

render(&lt;UserProfile userId=&quot;1&quot; /&gt;);

const errorMessage = await screen.findByText(/não encontrado/i);

expect(errorMessage).toBeInTheDocument();

});

});</code></pre>

<p>Para dados complexos ou múltiplas requisições, considere usar factory functions:</p>

<pre><code class="language-javascript">// src/mocks/factories.js

export function createUser(overrides = {}) {

return {

id: &#039;1&#039;,

name: &#039;Padrão&#039;,

email: &#039;padrao@example.com&#039;,

role: &#039;user&#039;,

...overrides

};

}

export function createApiError(status = 500, message = &#039;Erro&#039;) {

return { status, message };

}</code></pre>

<p>Outro padrão importante é mockar módulos locais. Use <code>vi.mock()</code> para casos onde você precisa controlar comportamentos de utilidades ou serviços:</p>

<pre><code class="language-javascript">import { vi } from &#039;vitest&#039;;

import { calculateDiscount } from &#039;./utils&#039;;

vi.mock(&#039;./utils&#039;, () =&gt; ({

calculateDiscount: vi.fn(() =&gt; 0.20)

}));

it(&#039;aplica desconto correto&#039;, () =&gt; {

expect(calculateDiscount(100)).toBe(0.20);

});</code></pre>

<h2>Boas Práticas e Anti-padrões</h2>

<p>Teste comportamentos, não implementações. Evite testar estado interno ou métodos privados — isso torna testes frágeis e acoplados. Quando um teste quebra porque você refatorou componentes internos (mantendo a funcionalidade), isso sinaliza um teste mal escrito.</p>

<p>Sempre use <code>screen</code> em vez de <code>render().container</code>. A Testing Library remove acesso direto ao DOM exatamente para encorajar consultas acessíveis. Nunca faça:</p>

<pre><code class="language-javascript"></code></pre>

<p>Aguarde elementos assincronamente com <code>findBy</code> ao invés de <code>getBy</code> para requisições:</p>

<pre><code class="language-javascript"></code></pre>

<h2>Conclusão</h2>

<p>Testes em React com Testing Library, MSW e estratégias de mock consolidam três pilares: <strong>1)</strong> Testing Library força testes centrados no usuário, eliminando testes frágeis acoplados à implementação; <strong>2)</strong> MSW fornece interceptação de rede real sem poluir seu código, sendo reutilizável entre testes e desenvolvimento; <strong>3)</strong> Mocking granular com <code>server.use()</code> e <code>vi.mock()</code> oferece controle fino sem sacrificar clareza. Domine esses fundamentos e seus testes se tornarão uma segurança real, não apenas cobertura numérica.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://testing-library.com/docs/react-testing-library/intro/" target="_blank" rel="noopener noreferrer">Testing Library Documentation</a></li>

<li><a href="https://mswjs.io/" target="_blank" rel="noopener noreferrer">Mock Service Worker - MSW Official Docs</a></li>

<li><a href="https://vitest.dev/" target="_blank" rel="noopener noreferrer">Vitest - Unit Testing Framework</a></li>

<li><a href="https://kentcdodds.com/blog/common-mistakes-with-react-testing-library" target="_blank" rel="noopener noreferrer">Common mistakes with React Testing Library - Kent C. Dodds</a></li>

<li><a href="https://martinfowler.com/articles/practical-test-pyramid.html" target="_blank" rel="noopener noreferrer">The Practical Test Pyramid - Martin Fowler</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Concorrência Real em JavaScript: Coordenando Múltiplas Promises na Prática
Concorrência Real em JavaScript: Coordenando Múltiplas Promises na Prática

O Que é Concorrência Real em JavaScript? Diferente da concorrência &quot;fals...

Guia Completo de Async Generators e Async Iterators em JavaScript na Prática
Guia Completo de Async Generators e Async Iterators em JavaScript na Prática

Entendendo Async Iterators Um async iterator é um objeto que implementa o pro...

Design Patterns em JavaScript: Strategy, Decorator e Composite: Do Básico ao Avançado
Design Patterns em JavaScript: Strategy, Decorator e Composite: Do Básico ao Avançado

Strategy Pattern: Flexibilidade no Comportamento O Strategy Pattern encapsula...