앞의 유닛 테스트 글의 마지막에 적어두었던 강의의 테스트코드와 같이 너무 많은 의존성들에 대하여 전부 mocking 처리하게되면 테스트의 신뢰성이 떨어지게 된다. 이럴 경우 어느정도는 실제 동작을 검증할 수 있게 묶어서 테스트를 하는걸 통합 테스트라고 보면 된다.
유닛테스트에 비해 신뢰성은 올라가지만 테스트 시간이 더 오래걸리며, 가짜 데이터를 만들어야 하고, 복잡성이 높아지는 단점이 있다.
- 세팅
// package.json
"test:integration": "jest --testRegex='.*\\integration.spec\\.ts$'",
통합 테스트용 명령어 추가 (파일이름을 integration.spec이 들어가게 지어줘야한다)
yarn add --dev sqlite3
실제 DB 대신 메모리에 가볍가 올려서 테스트 할 용도로 설치
아래는 강의에서 작성한 코드로 예시로서 보자.
일단 DB를 메모리에 띄워서 테스트하는 방법이 적혀있다.
그런데 e2e 테스트와 겹치는 부분이 많은 것 같다.
그래서 개인적인생각으로는 유닛 테스트와 e2e 테스트 두가지만 해도 충분하지 않을까 한다.
결국 애매한 포지션의 테스트라는게 개인적인 생각이다.
++ 이론상 배웠을 때는 애매한 포지션이었는데 막상 써보니 그건 아닌듯 싶다.
우선 유닛은 mock으로 테스트하고, e2e는 api를 기준으로 테스트하다보니 그에 해당안되는 테스트를 하기 좋다.
나의 경우 회원탈퇴 후 7일이 지난(softDeleted) 유저를 하루에 한번 db에서 삭제하는 Cron을 추가하면서 이게 제대로 작동하는지 테스트해보기 위해 통합테스트 코드를 작성했다.
이렇게 DB와 원하는 작업을 하는지 확인하는 면에 있어서 좋았고, 실제 동작과 거의 같기 떄문에 확실히 테스트의 신뢰성이 올라갔다.
그런데 통합테스트를 해보고나니 유닛테스트의 mock처리를 하지않고 통합 테스트처럼 메모리 DB로 테스트해보는게 더 좋을것 같다는 생각이 들면서, 강의에서 말해주신 유닛테스트와 통합테스트의 애매한 경계에 대하여 실감하게 되었다.
+++
https://springdream0406.tistory.com/224
Test Code 2
통합테스트 코드를 작성해본 후, 대부분을 mock으로 처리해놓은 현재 유닛테스트에 대한 의문을 가지고 있던 중, https://tech.inflab.com/20230404-test-code/ 테스트 코드를 왜 그리고 어떻게 작성해야 할
springdream0406.tistory.com
++
https://springdream0406.tistory.com/222
Test Code의 module
테스트코드를 강의만 본 후 따라서 만들었다 보니 코드에 대한 이해도가 부족했었다.그 가장 큰 예가 test에서 만드는 module이다.솔직히 그부분은 그냥 강의 따라서 적었다 보니 크게 신경쓰지 않
springdream0406.tistory.com
// movie.service.integration.spec.ts
describe('MovieService - Integration Test', () => {
let service: MovieService;
let cacheManager: Cache;
let dataSource: DataSource;
let users: User[];
let directors: Director[];
let movies: Movie[];
let genres: Genre[];
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
CacheModule.register(),
TypeOrmModule.forRoot({
type: 'sqlite',
database: ':memory:',
dropSchema: true, // 연결할 때 마다 db drop 할껀지
entities: [Movie, MovieDetail, Director, Genre, User, MovieUserLike],
synchronize: true,
logging: false,
}),
TypeOrmModule.forFeature([
Movie,
MovieDetail,
Director,
Genre,
User,
MovieUserLike,
]),
],
providers: [MovieService, CommonService],
}).compile();
service = module.get<MovieService>(MovieService);
cacheManager = module.get<Cache>(CACHE_MANAGER);
dataSource = module.get<DataSource>(DataSource);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
afterAll(async () => {
await dataSource.destroy();
});
beforeEach(async () => {
await cacheManager.reset();
const movieRepository = dataSource.getRepository(Movie);
const movieDetailRepository = dataSource.getRepository(MovieDetail);
const userRepository = dataSource.getRepository(User);
const directorRepository = dataSource.getRepository(Director);
const genreRepository = dataSource.getRepository(Genre);
// 가짜 데이터 생성
users = [1, 2].map((x) =>
userRepository.create({
id: x,
email: `${x}@test.com`,
password: `123123`,
}),
);
await userRepository.save(users);
directors = [1, 2].map((x) =>
directorRepository.create({
id: x,
dob: new Date('1992-11-23'),
nationality: 'South Korea',
name: `Director Name ${x}`,
}),
);
await directorRepository.save(directors);
genres = [1, 2].map((x) =>
genreRepository.create({
id: x,
name: `Genre ${x}`,
}),
);
await genreRepository.save(genres);
movies = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15].map((x) =>
movieRepository.create({
id: x,
title: `Movie ${x}`,
creator: users[0],
genres: genres,
likeCount: 0,
dislikeCount: 0,
detail: movieDetailRepository.create({
detail: `Movie Detail ${x}`,
}),
movieFilePath: 'moves/movie1.mp4',
director: directors[0],
createdAt: new Date(`2023-9-${x}`),
}),
);
await movieRepository.save(movies);
});
describe('findRecent', () => {
it('should return recent movies', async () => {
const result = (await service.findRecent()) as Movie[];
// 예측 데이터 만들기
const sortedResult = [...movies];
sortedResult.sort(
(a, b) => b.createdAt.getTime() - a.createdAt.getTime(),
);
const sortedResultIds = sortedResult.slice(0, 10).map((x) => x.id);
expect(result).toHaveLength(10);
expect(result.map((x) => x.id)).toEqual(sortedResultIds);
});
it('should cache recent movies', async () => {
const result = (await service.findRecent()) as Movie[];
const cachedData = await cacheManager.get('MOVIE_RECENT');
expect(cachedData).toEqual(result);
});
});
describe('findOne', () => {
it('should return movie correctly', async () => {
const movieId = movies[0].id;
const result = await service.findOne(movieId);
expect(result.id).toBe(movieId);
});
it('should throw NotFoundException if movie does noe exist', async () => {
await expect(service.findOne(99999)).rejects.toThrow(NotFoundException);
});
});
describe('findAll', () => {
it('should return movies with correct titles', async () => {
const dto = {
title: 'Movie 15',
order: ['createdAt_DESC'],
take: 10,
};
const result = await service.findAll(dto);
expect(result.data).toHaveLength(1);
expect(result.data[0].title).toBe(dto.title);
expect(result.data[0]).not.toHaveProperty('likeStatus');
});
it('should return likeStatus if userId is provided', async () => {
const dto = { order: ['createdAt_ASC'], take: 10 };
const result = await service.findAll(dto, users[0].id);
expect(result.data).toHaveLength(10);
expect(result.data[0]).toHaveProperty('likeStatus');
});
});
describe('create', () => {
beforeEach(() => {
jest.spyOn(service, 'renameMovieFile').mockResolvedValue();
});
it('should create movie correctly', async () => {
const createMovieDto: CreateMovieDto = {
title: 'Test Movie',
detail: 'A Test Movie Detail',
directorId: directors[0].id,
genreIds: genres.map((x) => x.id),
movieFileName: 'test.mp4',
};
const result = await service.create(
createMovieDto,
users[0].id,
dataSource.createQueryRunner(),
);
expect(result.title).toBe(createMovieDto.title);
expect(result.director.id).toBe(createMovieDto.directorId);
expect(result.genres.map((g) => g.id)).toEqual(genres.map((g) => g.id));
expect(result.detail.detail).toBe(createMovieDto.detail);
});
});
describe('update', () => {
it('should update movie correctly', async () => {
const movieId = movies[0].id;
const updateMovieDto: UpdateMovieDto = {
title: 'Changed Title',
detail: 'Changed Detail',
directorId: directors[1].id,
genreIds: [genres[0].id],
};
const result = await service.update(movieId, updateMovieDto);
expect(result.title).toBe(updateMovieDto.title);
expect(result.detail.detail).toBe(updateMovieDto.detail);
expect(result.director.id).toBe(updateMovieDto.directorId);
expect(result.genres.map((x) => x.id)).toEqual(updateMovieDto.genreIds);
});
it('should throw error if movie deos not exist', async () => {
const updateMovieDto: UpdateMovieDto = {
title: 'Change',
};
await expect(service.update(99999, updateMovieDto)).rejects.toThrow(
NotFoundException,
);
});
});
describe('remove', () => {
it('should remove movie correctly', async () => {
const removeId = movies[0].id;
const result = await service.remove(removeId);
expect(result).toBe(removeId);
});
it('should throw error if movie does not exist', async () => {
await expect(service.remove(99999)).rejects.toThrow(NotFoundException);
});
});
describe('toggleMovieLike', () => {
it('should create like correctly', async () => {
const userId = users[0].id;
const moviedId = movies[0].id;
const result = await service.toggleMovieLike(moviedId, userId, true);
expect(result).toEqual({ isLike: true });
});
it('should create dislike correctly', async () => {
const userId = users[0].id;
const moviedId = movies[0].id;
const result = await service.toggleMovieLike(moviedId, userId, false);
expect(result).toEqual({ isLike: false });
});
it('should toggle like correctly', async () => {
const userId = users[0].id;
const moviedId = movies[0].id;
await service.toggleMovieLike(moviedId, userId, true);
const result = await service.toggleMovieLike(moviedId, userId, true);
expect(result.isLike).toBeNull();
});
it('should toggle dislike correctly', async () => {
const userId = users[0].id;
const moviedId = movies[0].id;
await service.toggleMovieLike(moviedId, userId, false);
const result = await service.toggleMovieLike(moviedId, userId, false);
expect(result.isLike).toBeNull();
});
});
});
'코딩 > Nest.js' 카테고리의 다른 글
Testing - End to End Test (e2e 테스트) (1) | 2024.11.10 |
---|---|
Testing - Unit Test (단위 테스트) (0) | 2024.11.05 |
Testing - 개념과 초기 세팅 (3) | 2024.11.03 |
Swagger 2 (0) | 2024.10.27 |
Versioning (0) | 2024.10.27 |