28 Novembro, 2023 |
Por: João Miranda
28 Novembro, 2023 |
Por: João Miranda
Teste unitário é um tipo de teste de software que verifica o comportamento de uma unidade de código isoladamente. Uma unidade de código pode ser um método, uma função ou uma classe. Os testes unitários são importantes para o desenvolvimento de software por vários motivos, incluindo:
Garantia da qualidade: Os testes unitários ajudam a garantir que o código funcione conforme o esperado. Eles podem ajudar a identificar erros e falhas antes que eles sejam lançados no software.
Redução de custos: Esse tipo de teste ajuda a reduzir os custos de desenvolvimento e manutenção de software. Ele evita que erros sejam encontrados no final do processo de desenvolvimento, quando são mais caros para corrigir.
Melhoria da produtividade: Os testes unitários também melhoram a produtividade dos desenvolvedores, ajudando-os a escrever códigos mais confiáveis e corrigindo erros mais rapidamente.
O Vitest é um framework de teste unitário JavaScript/TypeScript moderno e fácil de usar. Ele é baseado no Jest, mas oferece recursos adicionais, como:
Testes síncronos e assíncronos: Adequado para testar código que usa APIs ou outras dependências assíncronas.
Testes de integração: Testam a interação entre unidades de código.
Testes de cobertura: O Vitest fornece relatórios de cobertura de código, que podem ajudar os desenvolvedores a identificar áreas de código que não estão sendo testadas.
Por esses motivos, o Vitest é uma boa escolha para realizar testes unitários em APIs. Ele é fácil de usar, oferece recursos abrangentes e é compatível com JavaScript e TypeScript.
A API que estamos utilizando no exemplo é uma API minimalista desenvolvida com Fastify e TypeScript para gerenciar operações básicas de um serviço de usuário. A API oferece duas principais funcionalidades: listar todos os usuários cadastrados (GET /users) e criar novos usuários, evitando duplicatas por meio do nome de usuário (POST /users). A autenticação é implementada de maneira simples, exigindo um token de autorização para a criação de usuários. A lógica de negócios está encapsulada na classe UserService, que mantém os usuários em memória. Erros são tratados de forma consistente usando a classe AppError. Este exemplo fornece uma base para a implementação de testes unitários visando garantir o correto funcionamento da API em diferentes cenários.
É importante mencionar que a API de exemplo é simplificada para facilitar a compreensão dos princípios de teste. A implementação real de uma API pode envolver mais complexidade, dependendo dos requisitos específicos do projeto.
import { test, expect, describe, beforeAll } from "vitest"
import { UserService } from "../user.service"
Aqui estamos importando as funções de teste (test), asserções (expect), descrição (describe) e beforeAll do framework de teste "vitest". Além disso, estamos importando a classe UserService de um módulo chamado "user.service".
let userService: UserService;
beforeAll(() => {
userService = new UserService();
})
Nesta parte, estamos declarando uma variável userService do tipo UserService e, no bloco beforeAll, está instanciando a classe UserService antes de executar os testes. O beforeAll é um gancho que executa uma vez antes de todos os testes no escopo do describe.
describe("User Service", () => {
// ... test cases ...
});
Aqui, estamos usando o describe para agrupar os testes relacionados ao serviço de usuário. Dentro deste bloco, você tem vários testes individuais
Este teste verifica se é possível criar um usuário com sucesso, usando o método create do UserService. Ele verifica se o resultado possui uma propriedade 'id' e se o nome de usuário é o esperado.
test('Deve ser possível criar um usuário', () => {
Esta linha define um teste com uma descrição clara e legível: "Deve ser possível criar um usuário".
const result = userService.create({
name: 'User Test',
username: 'user_test'
})
Aqui, estamos chamando o método create da instância do UserService, passando um objeto de usuário como argumento. O objeto de usuário possui um nome e um nome de usuário.
expect(result).toHaveProperty('id')
expect(result.username).toBe('user_test')
Estas linhas definem as expectativas ou asserções do teste:
expect(result).toHaveProperty('id'): Verifica se o objeto resultante possui uma propriedade 'id'. Isso implica que o usuário foi criado com sucesso, pois um 'id' foi atribuído a ele durante o processo de criação.
expect(result.username).toBe('user_test'): Garante que o nome de usuário do usuário criado seja igual a 'user_test'.
Dessa forma, esse teste específico verifica se a criação de um usuário com determinado nome e nome de usuário ocorre conforme o esperado, validando a presença de um 'id' e o nome de usuário associado ao usuário criado.
Este teste verifica se é lançada uma exceção quando se tenta criar um usuário com um nome de usuário que já existe. Ele usa toThrow para garantir que a exceção esperada seja lançada.
test('Não deve ser possível criar um usuário já existente', () => {
A descrição do teste é "Não deve ser possível criar um usuário já existente".
userService.create({
name: 'User Test',
username: 'user_test_already_exists'
})
Nesta linha, um usuário é criado com um nome de usuário único, 'user_test_already_exists'.
expect(() => {
userService.create({
name: 'User Test',
username: 'user_test_already_exists'
})
}).toThrow('User already exists')
Esta parte do teste usa a função expect para verificar se a criação de um usuário com um nome de usuário que já existe gera uma exceção. O bloco de código dentro de expect(() => {...}) representa a tentativa de criar um usuário duplicado.
toThrow('User already exists'): Certifica-se de que a exceção gerada tem a mensagem "User already exists", indicando que o sistema está tratando corretamente a tentativa de criar um usuário com um nome de usuário que já está em uso.
Este teste verifica se é possível obter uma lista de usuários usando o método findAll do UserService e se o resultado possui o comprimento esperado.
test('Deve ser possível recuperar a lista de usuários', () => {
A descrição do teste é "Deve ser possível recuperar a lista de usuários".
const result = userService.findAll();
Aqui, estamos chamando o método findAll da instância do UserService. Este método é projetado para retornar a lista de todos os usuários cadastrados.
expect(result).toHaveLength(2)
Esta linha verifica se o resultado (a lista de usuários) possui um comprimento (tamanho) esperado de 2. Isso implica que, de acordo com o contexto do teste, dois usuários foram criados e estão presentes na lista.
import { test, describe } from "vitest"
import request from "supertest";
import { app } from "../../../app";
Aqui estamos importando as funções de teste (test), descrição (describe) do framework de teste "vitest". Também importando a função de teste HTTP request do “supertest”. Além disso, estamos importando a classe UserService de um módulo chamado "user.service".
describe('User E2E', () => {
// ... test cases ...
});
Usa o describe para agrupar os testes relacionados à interação de usuário do ponto de vista do cliente (end-to-end).
Este teste verifica se é possível criar um usuário com sucesso, usando o método create do UserService. Ele verifica se a rota da o código esperado.
test('Deve ser possível criar um usuário', async () => {
A descrição do teste indica que o objetivo é verificar se é possível criar um usuário.
await app.ready();
Antes de realizar a requisição, aguarda o momento em que a aplicação está pronta. Isso é especialmente importante em testes end-to-end para garantir que a aplicação esteja totalmente inicializada.
await request(app.server)
.post('/users')
.set('Authorization', 'Bearer TOKEN_FAKE')
.send({
name: 'User Test E2E',
username: 'user_test_e2e'
}).expect(200)
Estas linhas definem as expectativas ou asserções do teste:
expect(result).toHaveProperty('id'): Verifica se o objeto resultante possui uma propriedade 'id'. Isso implica que o usuário foi criado com sucesso, pois um 'id' foi atribuído a ele durante o processo de criação.
expect(result.username).toBe('user_test'): Garante que o nome de usuário do usuário criado seja igual a 'user_test'.
Dessa forma, esse teste específico verifica se a criação de um usuário com determinado nome e nome de usuário ocorre conforme o esperado, validando a presença de um 'id' e o nome de usuário associado ao usuário criado.
A importância dos testes unitários vai além de uma simples verificação de funcionalidades, desempenhando um papel crucial na garantia da qualidade do código, na redução de custos ao evitar a identificação de erros tardiamente no desenvolvimento e na melhoria da produtividade dos programadores. O Vitest, escolhido para conduzir os testes unitários neste contexto, demonstra ser uma opção robusta, oferecendo recursos abrangentes, como suporte a testes síncronos e assíncronos, testes de integração e relatórios de cobertura de código.
Além disso, a abordagem prática exemplificada no código de teste E2E também enfatiza a importância da validação completa do sistema. Os testes E2E, ao simular interações do usuário do ponto de vista do cliente, proporcionam uma visão abrangente do funcionamento da API. A combinação de testes unitários e E2E contribui para um processo de desenvolvimento mais confiável. Ao adotar uma abordagem abrangente de teste, os programadores podem identificar e corrigir problemas em diferentes camadas da aplicação, assegurando um software escalável e eficiente.
0 Comentários