نحوه نوشتن تست های واحد برای سرویس های Backend با وابستگی های پایگاه داده با استفاده از SQLite In-Memory

معرفی
هنگام توسعه خدمات باطن، تست واحد برای اطمینان از صحت و ثبات کد شما بسیار مهم است. با این حال، نوشتن آزمونهای واحد برای مؤلفههایی که با پایگاه داده تعامل دارند، میتواند چالش برانگیز باشد. استفاده از یک پایگاه داده واقعی برای آزمایش می تواند کند و دست و پا گیر باشد و می تواند عوارض جانبی ایجاد کند که تکرار آزمایش ها را دشوار می کند. یک راه حل موثر، استفاده از پایگاه داده SQLite در حافظه است که راه اندازی سریع و آسان است و امکان جداسازی و تکرار تست ها را فراهم می کند.
در این مقاله، نحوه پیکربندی و نوشتن تستهای واحد را برای یک سرویس پشتیبان که با پایگاه داده تعامل دارد، با استفاده از TypeORM و SQLite درون حافظه بررسی خواهیم کرد.
نصب وابستگی ها
ابتدا باید وابستگی های لازم را نصب کنیم:
npm install --save-dev typescript ts-jest ts-node @types/jest @types/node jest sqlite3 typeorm reflect-metadata
پیکربندی محیط تست
فایل پیکربندی TypeORM برای آزمایش
یک فایل پیکربندی TypeORM خاص برای آزمایش ایجاد کنید. این فایل TypeORM را برای استفاده از پایگاه داده SQLite در حافظه پیکربندی می کند.
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);
});
ساختار پروژه
فرض کنید ساختار پروژه زیر را دارید:
src/
entity/
User.ts
repository/
UserRepository.ts
service/
UserService.ts
__tests__/
UserService.test.ts
jest.setup.ts
پیاده سازی ماژول ها
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);
}
}
تست پیکربندی با Jest
یک فایل پیکربندی Jest ایجاد کنید تا مطمئن شوید که محیط تست شما به درستی پیکربندی شده است.
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',
},
},
};
پیکربندی TypeScript
از پیکربندی TypeScript اطمینان حاصل کنید (tsconfig.json
) امکان استفاده از دکوراتورها و فراداده های دکوراتور را می دهد.
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"]
}
تست نویسی
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();
});
});
توضیح
-
تست پیکربندی پایگاه داده:
- ما TypeORM را برای استفاده از پایگاه داده SQLite در حافظه برای آزمایش پیکربندی کردیم.
-
jest.setup.ts
برای ایجاد اتصال پایگاه داده قبل از همه آزمایش ها و بستن آن پس از همه آزمایش ها استفاده می شود.
-
همگام سازی پایگاه داده:
- پس از هر تست، پایگاه داده را همگام سازی می کنیم تا داده های وارد شده در طول آزمایش پاک شود (
await getConnection().synchronize(true);
).
- پس از هر تست، پایگاه داده را همگام سازی می کنیم تا داده های وارد شده در طول آزمایش پاک شود (
-
تست نویسی:
- ما تست هایی ایجاد می کنیم تا بررسی کنیم که کاربر به درستی ایجاد شده است، کاربر با نام پیدا شده است و کاربر برمی گردد.
undefined
زمانی که کاربر پیدا نشد
- ما تست هایی ایجاد می کنیم تا بررسی کنیم که کاربر به درستی ایجاد شده است، کاربر با نام پیدا شده است و کاربر برمی گردد.
نتیجه
استفاده از پایگاه داده SQLite در حافظه برای آزمایش، یک راه عالی برای آزمایش عملکرد است که به یک پایگاه داده واقعی بدون پیچیدگی راه اندازی یک پایگاه داده آزمایشی جداگانه بستگی دارد. این تضمین می کند که تست ها سریع، ایزوله و قابل اعتماد هستند. با این رویکرد، می توانید اطمینان حاصل کنید که کد شما به درستی با پایگاه داده تعامل دارد و تمام عملکردهای حیاتی تأیید شده است.
مخزن
https://github.com/vitorrios1001/tests-with-db