Como Escrever Testes Unitários para Serviços Backend com Dependências de Banco de Dados Usando SQLite In-Memory

Introdução

Ao desenvolver serviços backend, os testes unitários são cruciais para garantir a correção e a estabilidade do seu código. No entanto, escrever testes unitários para componentes que interagem com um banco de dados pode ser desafiador. Usar um banco de dados real para testes pode ser lento e complicado, além de introduzir efeitos colaterais que dificultam a reprodução dos testes. Uma solução eficaz é usar um banco de dados SQLite in-memory, que é rápido e fácil de configurar, permitindo que os testes sejam isolados e repetíveis.

Neste artigo, vamos explorar como configurar e escrever testes unitários para um serviço backend que interage com um banco de dados, usando TypeORM e SQLite in-memory.

Instalação das Dependências

Primeiro, precisamos instalar as dependências necessárias:

npm install --save-dev typescript ts-jest ts-node @types/jest @types/node jest sqlite3 typeorm reflect-metadata
Enter fullscreen mode
Exit fullscreen mode

Configuração do Ambiente de Testes

Arquivo de Configuração do TypeORM para Testes

Crie um arquivo de configuração do TypeORM específico para testes. Este arquivo configura o TypeORM para usar um banco de dados SQLite em memória.

jest.setup.ts

import 'reflect-metadata';
import { createConnection, getConnection,
} from 'typeorm';
import { User } from './src/entity/User'; beforeAll(() => { return createConnection({ type: 'sqlite', database: ':memory:', dropSchema: true, entities: [User], synchronize: true, logging: false, });
}); afterAll(async () => { const connection = getConnection(); await connection.close();
}); afterEach(async () => { const connection = getConnection(); await connection.synchronize(true);
});
Enter fullscreen mode
Exit fullscreen mode

Estrutura do Projeto

Suponha que você tenha a seguinte estrutura de projeto:

src/ entity/ User.ts repository/ UserRepository.ts service/ UserService.ts __tests__/ UserService.test.ts jest.setup.ts
Enter fullscreen mode
Exit fullscreen mode

Implementação dos Módulos

src/entity/User.ts

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity()
export class User { @PrimaryGeneratedColumn() id!: number; @Column() name!: string; @Column() email!: string;
}
Enter fullscreen mode
Exit fullscreen mode

src/repository/UserRepository.ts

import { EntityRepository, Repository } from 'typeorm';
import { User } from '../entity/User'; @EntityRepository(User)
export class UserRepository extends Repository<User> { findByName(name: string): Promise<User | undefined> { return this.findOne({ name }); }
}
Enter fullscreen mode
Exit fullscreen mode

src/service/UserService.ts

import { getCustomRepository } from 'typeorm';
import { UserRepository } from '../repository/UserRepository';
import { User } from '../entity/User'; export class UserService { private userRepository = getCustomRepository(UserRepository); async findUserByName(name: string): Promise<User | undefined> { return this.userRepository.findByName(name); } async createUser(name: string, email: string): Promise<User> { const user = new User(); user.name = name; user.email = email; return this.userRepository.save(user); }
}
Enter fullscreen mode
Exit fullscreen mode

Configuração de Testes com Jest

Crie um arquivo de configuração do Jest para garantir que o ambiente de testes está configurado corretamente.

jest.config.js

module.exports = { preset: 'ts-jest', testEnvironment: 'node', setupFilesAfterEnv: ['./jest.setup.ts'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], testPathIgnorePatterns: ['/node_modules/', '/dist/'], transform: { '^.+\\.(ts|tsx)$': 'ts-jest', }, globals: { 'ts-jest': { tsconfig: 'tsconfig.json', }, },
};
Enter fullscreen mode
Exit fullscreen mode

Configuração do TypeScript

Certifique-se de que a configuração do TypeScript (tsconfig.json) permite o uso de decoradores e metadados de decoradores.

tsconfig.json

{ "compilerOptions": { "target": "ES2020", "module": "CommonJS", "lib": ["ES2020"], "outDir": "./dist", "rootDir": "./src", "strict": true, "esModuleInterop": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "moduleResolution": "node", "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "resolveJsonModule": true }, "include": ["src/**/*.ts"], "exclude": ["node_modules", "**/*.test.ts", "dist"]
}
Enter fullscreen mode
Exit fullscreen mode

Escrita dos Testes

src/__tests__/UserService.test.ts

import { UserService } from '../service/UserService'; describe('UserService', () => { let userService: UserService; beforeAll(() => { userService = new UserService(); }); test('should create a new user', async () => { const user = await userService.createUser('John Doe', '[email protected]'); expect(user).toHaveProperty('id'); expect(user.name).toBe('John Doe'); expect(user.email).toBe('[email protected]'); }); test('should find a user by name', async () => { const user = await userService.createUser('Jane Doe', '[email protected]'); const foundUser = await userService.findUserByName('Jane Doe'); expect(foundUser).toBeDefined(); expect(foundUser?.name).toBe('Jane Doe'); expect(foundUser?.email).toBe('[email protected]'); }); test('should return undefined if user is not found', async () => { const foundUser = await userService.findUserByName('Non Existent'); expect(foundUser).toBeUndefined(); });
});
Enter fullscreen mode
Exit fullscreen mode

Explicação

  1. Configuração do Banco de Dados de Teste:

    • Configuramos o TypeORM para usar um banco de dados SQLite em memória para testes.
    • jest.setup.ts é usado para criar a conexão com o banco de dados antes de todos os testes e fechá-la após todos os testes.
  2. Sincronização do Banco de Dados:

    • Após cada teste, sincronizamos o banco de dados para limpar os dados inseridos durante o teste (await getConnection().synchronize(true);).
  3. Escrita dos Testes:

    • Criamos testes para verificar se o usuário é criado corretamente, se o usuário é encontrado pelo nome e se retorna undefined quando o usuário não é encontrado.

Conclusão

Usar um banco de dados SQLite in-memory para testes é uma ótima maneira de testar funcionalidades que dependem de um banco de dados real sem a complexidade de configurar um banco de dados de teste separado. Isso garante que os testes sejam rápidos, isolados e confiáveis. Com esta abordagem, você pode garantir que seu código interaja corretamente com o banco de dados e que todas as funcionalidades críticas sejam verificadas.

Repositório

https://github.com/vitorrios1001/tests-with-db


Discover more from Coursity

Subscribe to get the latest posts sent to your email.

Leave a Comment

Your email address will not be published. Required fields are marked *

Discover more from Coursity

Subscribe now to keep reading and get access to the full archive.

Continue reading

Scroll to Top
How to minimize app size in flutter for faster downloads and updates. Essential skills for web developer success. * add your javascript code here.