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
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);
});
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
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;
}
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 });
}
}
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);
}
}
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',
},
},
};
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"]
}
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', 'john@example.com');
expect(user).toHaveProperty('id');
expect(user.name).toBe('John Doe');
expect(user.email).toBe('john@example.com');
});
test('should find a user by name', async () => {
const user = await userService.createUser('Jane Doe', 'jane@example.com');
const foundUser = await userService.findUserByName('Jane Doe');
expect(foundUser).toBeDefined();
expect(foundUser?.name).toBe('Jane Doe');
expect(foundUser?.email).toBe('jane@example.com');
});
test('should return undefined if user is not found', async () => {
const foundUser = await userService.findUserByName('Non Existent');
expect(foundUser).toBeUndefined();
});
});
Explicação
-
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.
-
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);
).
- Após cada teste, sincronizamos o banco de dados para limpar os dados inseridos durante o teste (
-
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.
- Criamos testes para verificar se o usuário é criado corretamente, se o usuário é encontrado pelo nome e se retorna
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.
Top comments (0)