NodeJS
Jest

Implementando Testes com Jest: Garantindo a Confiabilidade do Código

Adotei o Jest pra testar meu código e ganhei muito em confiança e qualidade. Essa jornada me fez enxergar o valor real do TDD no dia a dia.

Última atualização realizada em:
Imagem do autor do blog Yago Marinho
Por Yago Marinho
Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui

Estava explorando algumas discussões sobre tecnologia e mercado no Reddit quando encontrei a seguinte pergunta de um usuário: “Quão utilizado na indústria é o TDD (Test Driven Development)?”. A resposta foi tão boa quanto a dúvida: “o termo (TDD) foi tão banalizado que, hoje, usam isso com a ideia de que 'Testou?! Então é TDD!', quando na verdade não é.”
Essa troca me fez refletir. No código que tenho desenvolvido, a prática não é TDD — e o motivo é simples: escrevi a aplicação primeiro e só depois criei os testes. Se o nome da abordagem é literalmente “desenvolvimento orientado a testes”, não faz sentido dizer que estou seguindo TDD quando o fluxo foi o inverso.
Mas, independentemente da nomenclatura, o ponto central é outro: testes são indispensáveis. Eles mantêm o código confiável enquanto o projeto cresce, e sem eles cada manutenção ou correção se torna uma caçada às cegas por possíveis erros.

Tabela de conteúdos

Por que escrever testes importa?

Testes não são um “extra” que colocamos no final do projeto. Eles são parte fundamental do ciclo de desenvolvimento porque permitem que o código evolua com segurança. Quando existe cobertura de testes, é possível refatorar sem medo de quebrar algo que já estava funcionando.
Outro ponto importante é que, à medida em que a aplicação cresce, a complexidade cresce junto. Encontrar um bug sem testes se torna cada vez mais custoso. Já com testes, qualquer mudança pode ser validada de forma rápida e previsível. Em resumo:
  • Eles aumentam a confiabilidade do sistema.
  • Tornam a manutenção mais fácil e menos arriscada.
  • Permitem refatorações seguras sem a sensação de “andar no escuro”.

Por que escolhi testar com Jest?

Para implementar testes, escolhi o Jest. A decisão foi simples: além de ser muito usado na comunidade JavaScript/TypeScript, o Jest é robusto e traz recursos que facilitam a vida no dia a dia.
Alguns pontos que pesaram na escolha:
  • Resultados legíveis em múltiplos formatos, facilitando entender falhas rapidamente.
  • Cache de resultados de testes, o que agiliza bastante quando a suíte cresce.
  • Mocks e spies nativos, que simplificam o isolamento de dependências.
  • Configuração mínima, funcionando bem desde o início sem precisar de muito setup.
É claro que eu poderia escrever testes em NodeJS puro, mas o Jest acelera a implementação, traz mais clareza nos relatórios e se adapta facilmente a diferentes cenários do projeto.

Exemplo prático

Você pode acessar o código completo do projeto diretamente no GitHub através deste link: clique aqui

Uma das maiores vantagens do Jest é justamente a capacidade de se adaptar ao código já existente. Não precisei reescrever funções ou refatorar a aplicação inteira só para começar a testar — bastou criar os testes em cima do que já estava pronto.
Observe como o Jest se adapta ao que já existe e facilita mocks/relatórios, no código abaixo:
// __tests__/get-post-list.spec.ts // - usa @notionhq/client com um Client({ auth }) // - consulta data_source_id "collection-123" // - aplica filtro { published: true } quando NODE_ENV === 'production' // - ordena por updated_at desc // - normaliza resultados, ignorando páginas no lixo, objetos que não são "page" e páginas sem props mínimas import { Client } from '@notionhq/client' import { getPostList } from '../src/services/get.posts.list' import { createPage } from './fakes/page.ts' // --- Mock da lib externa (@notionhq/client) jest.mock('@notionhq/client', () => { const mockQuery = jest.fn() const MockClient = jest.fn().mockImplementation(() => ({ dataSources: { query: mockQuery, }, })) // expõe o mock para uso no teste ;(MockClient as any).__mockQuery = mockQuery return { Client: MockClient } }) describe('get post list', () => { const mockQuery = (Client as any).__mockQuery as jest.Mock const OLD_ENV = process.env beforeEach(() => { jest.clearAllMocks() jest.useFakeTimers().setSystemTime(new Date('2025-01-01T12:00:00Z')) process.env = { ...OLD_ENV, NOTION_TOKEN: 'test-token' } }) afterAll(() => { process.env = OLD_ENV jest.useRealTimers() }) it('usa filtro published=true em produção', async () => { // força ambiente de produção para validar o filtro process.env.NODE_ENV = 'production' const results = [ createPage({}), // válido createPage({ id: 'trash', in_trash: true }), // deve ser ignorado createPage({ id: 'not-page', object: 'database' }), // deve ser ignorado createPage({ id: 'missing-props', withProps: false }), // deve ser ignorado // outro válido, com props vazias que sua normalização trata: createPage({ id: 'empty-props', slug: '', title: '', description: '', withProps: true, }), ] mockQuery.mockResolvedValueOnce({ results }) const list = await getPostList() // 1) o Client foi instanciado com o token esperado expect(Client).toHaveBeenCalledWith({ auth: 'test-token' }) // 2) a consulta foi feita com os parâmetros corretos em produção expect(mockQuery).toHaveBeenCalledWith({ data_source_id: 'collection-123', filter: { property: 'published', checkbox: { equals: true }, }, sorts: [{ property: 'updated_at', direction: 'descending' }], page_size: 10, }) // 3) a lista final contém apenas os itens válidos e normalizados expect(list).toHaveLength(2) expect(list[0]).toEqual({ slug: 'my-slug', title: 'My Title', description: 'My Description', created_at: expect.any(Date), updated_at: expect.any(Date), }) expect(list[1]).toEqual({ slug: '', title: '', description: '', created_at: expect.any(Date), updated_at: expect.any(Date), }) }) })
Esse padrão permite que eu controle totalmente as respostas da biblioteca sem precisar me conectar de fato à API da Notion. Assim, consigo validar o comportamento do meu código em diferentes cenários — sucesso, erro, exceções — apenas manipulando o mock.
Além disso, as asserções do Jest tornam a checagem dos resultados esperados muito clara (toBe, toEqual, toHaveBeenCalledWith, etc.), e os relatórios de execução mostram com precisão quais testes passaram, quais falharam e até a cobertura geral do projeto.
No fim, o que mais gosto é isso: com poucas linhas, consigo simular um ambiente inteiro e ter confiança de que meu código vai se comportar como esperado em produção.
Além deste, há diversos outros testes implementados na aplicação — todos seguindo uma abordagem semelhante: criar mocks, simular cenários e validar o comportamento esperado. A ideia é justamente não precisar refatorar o código por enquanto, aproveitando o fato de que o Jest se encaixa bem no fluxo já existente.

Evoluindo a mentalidade

Hoje, minha abordagem é simples: escrevo o código e depois crio os testes. Isso já traz benefícios imediatos de confiabilidade e reduz bastante o risco de regressões.
Mas o ponto é que, agora que os testes já existem, fica mais fácil inverter o processo: refatorar primeiro o teste e só depois o código. Essa mudança de perspectiva abre caminho para o TDD, onde cada funcionalidade nasce a partir de um teste que define o comportamento esperado.
Não se trata de virar a chave de uma hora para outra, mas de uma evolução natural. Aos poucos, essa prática transforma a forma como pensamos a implementação: em vez de apenas garantir que algo funciona, passamos a desenhar o código a partir de como ele será usado e validado.

Roadmap e próximos passos

  • Expandir a implementação de testes automatizados para outras áreas do projeto.
  • Melhorar o design para refletir um blog mais moderno e consistente.
  • Adicionar scripts de monitoramento de performance do conteúdo.
  • Utilizar uma ferramenta de bundle para otimizar o tamanho da aplicação dentro do monorepo.
  • Refatorar o código para desacoplar módulos e preparar a aplicação para futuras substituições sem grandes impactos.

Conclusão

Implementar testes não é apenas sobre aumentar a cobertura de código. É sobre criar uma base confiável que permita que o projeto evolua sem medo de quebrar o que já existe.
O Jest entrou aqui como uma ferramenta prática e robusta, que me permitiu validar comportamentos, mockar dependências externas e ganhar clareza com relatórios e asserções simples.
Mais do que isso, esses testes já abrem espaço para uma mudança de mentalidade: aos poucos, o fluxo pode migrar de escrever o código primeiro para desenhar o comportamento esperado via testes. Esse é o passo natural para chegar ao TDD.
O ponto central é que, independentemente da abordagem, a qualidade não pode ser opcional. Testar é parte do desenvolvimento — e é exatamente isso que garante confiabilidade, escalabilidade e evolução segura do projeto.