Mocking이란
테스트에서 실제 객체나 함수 대신에 가짜 객체나 함수를 사용하는 기법을 말한다. 주로 테스트 환경을 제어하고, 특정 상황을 시뮬레이션하며, 테스트의 정확성을 보장하기 위해 사용된다.
Mock 란 가짜 함수의 실행, 가짜 클래스의 실행이다.
Mocking의 주요 목적
TypeORM에서 진짜 유저 Repository를 불러오지 않고 유저 서비스를 단독으로 테스트하기 위해서이다.
- 의존성 제거: 테스트할 코드가 외부 시스템(예: 데이터베이스, API)과 상호작용할 때, 실제 시스템에 의존하지 않고 가짜 객체를 사용하여 독립적으로 테스트할 수 있다.
- 예측 가능한 결과: 가짜 객체나 함수의 동작을 명확하게 정의함으로써, 테스트 결과를 예측하고 일관되게 유지할 수 있다.
- 테스트의 안정성 향상: 외부 시스템이 불안정하거나 데이터가 변동될 경우에도 테스트가 영향을 받지 않도록 할 수 있다.
- 테스트 속도 향상: 실제 외부 시스템과의 상호작용이 없으므로 테스트 속도가 빨라질 수 있다.
코드 설명
import { Test } from "@nestjs/testing";
import { UserService } from "./user.service";
import { getRepositoryToken } from "@nestjs/typeorm";
import { User } from "./entities/user.entity";
import { Verification } from "./entities/verification.entity";
import { JwtService } from "src/jwt/jwt.service";
import { MailService } from "src/mail/mail.service";
import { Repository } from "typeorm";
const mockRepository = {
create: jest.fn(),
save: jest.fn(),
findOne: jest.fn(),
};
const mockJwtService = {
sign: jest.fn(),
verify: jest.fn(),
};
const mockMailService = {
sendVerificationEmail: jest.fn(),
};
type MockRepository<T = any> = Partial<
Record<keyof Repository<User>, jest.Mock>
>;
describe("UserService", () => {
let service: UserService;
let userRepository: MockRepository<User>;
beforeAll(async () => {
const module = await Test.createTestingModule({
providers: [
UserService,
{
// USerRepository
provide: getRepositoryToken(User),
useValue: mockRepository,
},
{
// VerificationRepository
provide: getRepositoryToken(Verification),
useValue: mockRepository,
},
{
// JwtService
provide: JwtService,
useValue: mockJwtService,
},
{
// mailService
provide: MailService,
useValue: mockMailService,
},
],
}).compile();
service = module.get<UserService>(UserService);
userRepository = module.get(getRepositoryToken(User));
});
it("should be defined", () => {
expect(service).toBeDefined();
});
describe("createAccount", () => {
const createAccountArgs = {
email: "test@mail.com",
password: "123",
role: UserRole.Client,
};
it("should fail if user exists", async () => {
userRepository.findOne.mockResolvedValue({
id: 1,
email: "test@mail.com",
});
const result = await service.createAccount(createAccountArgs);
expect(result).toMatchObject({
ok: false,
error: "There is a user with that email already",
});
});
});
it.todo("login");
it.todo("findById");
it.todo("verifyEmail");
});
위 코드는 전체 코드이다.
Test.createTestingModule() - 테스트 모듈을 설정
import { Test } from "@nestjs/testing";
const module = await Test.createTestingModule({({ ... }).compile();:
- Test.createTestingModule을 사용하여 테스트 모듈을 설정한다.
- 모듈에는 UserService와 UserRepository를 제공하는 설정이 포함된다.
- compile() 메서드는 모듈을 컴파일하여 테스트에 사용할 수 있도록 한다.
import { UserService } from "./user.service";
const mockRepository = () => ({
create: jest.fn(),
save: jest.fn(),
delete: jest.fn(),
findOne: jest.fn(),
findOneOrFail: jest.fn(),
});
type MockRepository<T = any> = Partial<
Record<keyof Repository<User>, jest.Mock>
>;
describe("UserService", () => {
let service: UserService;
let userRepository: MockRepository<User>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
UserService,
{
// USerRepository
provide: getRepositoryToken(User),
useValue: mockRepository(),
},
...
],
}).compile();
service = module.get<UserService>(UserService);
userRepository = module.get(getRepositoryToken(User));
})
})
providers: [ ... ]:
- providers 배열은 테스트 모듈에서 사용될 서비스와 그들의 의존성을 설정한다.
- UserService는 실제 서비스이다.
- UserRepository는 테스트용으로 모킹 된 객체로 제공된다.
useValue: { ... }:
- useValue는 UserRepository를 모킹 할 때 사용하는 방법이다.
- 실제 데이터베이스와 상호작용하지 않고, 테스트를 위해 가짜 기능을 제공한다.
- find 메서드는 항상 [{ id: 1, name: 'John' }]이라는 값을 반환
- save 메서드는 항상 { id: 1, name: 'John' }이라는 값을 반환
userService = module.get<UserService>(UserService);
- UserService 인스턴스를 테스트 모듈에서 가져와 userService 변수에 할당한다.
userRepository = module.get<UserRepository>(UserRepository);
- UserRepository 인스턴스를 테스트 모듈에서 가져와 userRepository 변수에 할당한다.
type MockRepository<T = any> = Partial< Record<keyof Repository<User>, jest.Mock> >;
- 레코드는 타입 T의 요소 K의 집합으로 타입을 만들어주는 TypeScript 다.
- let userRepository: Partial <Record<"hello", number>>;
- 예를 들어, 요소 K는 넘버 타입의 hello 같은 것이다
- let userRepository: Partial<Record <keyof Repository<User>, jest.Mock>>;
- Partial이 모든 요소를 optional로 만들어 준다.
- 레파지토리의 key를 가져오고 싶은 것이니깐 key of 붙여주기
- 타입이 jest.mock이다.
- 요소의 집합이란 건 User Repository의 모든 요소들을 말한다.
- 요소란 findOne, save, create, update 같은 모든 걸 말한다.
'Frontend > Jest' 카테고리의 다른 글
Jest로 단위 테스트 코드를 구현할 때 자주 사용되는 API (0) | 2024.08.01 |
---|---|
Jest 란 (0) | 2024.08.01 |