NestJS TrelloProject 컨트롤러, 서비스 테스트 파일 구성
> jest, @nestjs/testing 모듈 사용
1. lists.controller.spec.ts 구성
컨트롤러 테스트 > 컨트롤러가 요청을 받았을 때 서비스의 메서드를 올바르게 호출하는 지, 올바른 결과를 반환하는지 검증해야함
Jest 문법
1. describe() 함수
관련된 테스트 케이스들을 그룹화하는데 사용.
첫 번째 인자는 테스트그룹 명칭 문자열, 두 번째 인자는 해당 그룹에서 실행해야할 텟스트 케이스를 정의한 함수
describe('ListsController', () => {
// 여기에 ListsController 관련 테스트 케이스 작성
});
2. it 메서드
test() 함수와 함께 특정 개별 테스트 케이스를 정의하는데 사용, 동일한 기능 수행
첫번째 인자는 테스트 설명, 두 번째 인자는 테스트 실행 함수
it('리스트 생성 테스트', async () => {
3. expect
Jest의 단언 메서드. 특정 조건이 충족되는지 검사.
테스트 결과를 평가하는데 사용되는 일련의 메처 함수 포함
toBe(): 객체의 동일성(===)을 검사하는 매처 함수(값과 형식까지)
toEqual() : 객체의 동일성을 검사하는 매처 함수
toHaveBeenCalledWith() : 메서드가 특정 인자로 호출되었는지 검사
toBeUndefined(): 값이 undefined인지 검사
4. mockResolvedValue
모의(mock) 함수의 동작을 설정하는 Jest 메서드
실제 테스트 코드 작성
0. 모의객체 설정 및 초기화
- 실제 서비스 대신 모의객체를 사용하여 컨트롤러의 의존성을 주입
- 테스트 모듈 구성 시 실제 서비스 대신 mockListsService를 주입 >> 실제 서비스 구현 없이 테스트 가능
- beforeEach 블록이 각 테스트 케이스 실행 전 호출되어 위 테스트 모듈 설정 진행
- afterEach 블록이 각 테스트 케이스 완료 후 호출되어 모든 모의함수 호출 기록 초기화 >> 각 테스트가 독립적으로 실행
describe('ListsController', () => {
let controller: ListsController;
let service: ListsService;
// 서비스에 대한 모의 객체(mock)를 정의
const mockListsService = {
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
updatePositions: jest.fn(),
};
// beforeEach : 각 테스트 케이스 실행 전 호출
beforeEach(async () => {
// Test.createTestingModule 메서드로 모듈 생성
// controller, provider 설정
const module: TestingModule = await Test.createTestingModule({
controllers: [ListsController],
providers: [
{
provide: ListsService,
useValue: mockListsService,
},
],
}).compile();
// module.get 메서드를 사용해 설정한 모듈에서 ListsController, ListsService 인스턴스 가져옴
controller = module.get<ListsController>(ListsController);
service = module.get<ListsService>(ListsService);
});
afterEach(() => {
jest.clearAllMocks();
});
1. create 메서드
// src/lists/lists.controller.ts
...
@Post()
async create(@Body() createListDto: CreateListDto) {
return this.listsService.create(createListDto);
}
...
HTTP POST 요청이 들어오면 create 메서드가 실행.
클라이언트에서 전달된 body 데이터를 createListDto로 받고, 이를 서비스 create 메서드에 전달 후 결과 반환
describe('create', () => {
it('리스트 생성 테스트', async () => {
// 테스트 데이터 설정
const createListDto: CreateListDto = {
boardId: 1,
title: 'New List',
};
// result : 예상 결과 값
const result = { id: 1, boardId: 1, title: 'New List', position: 1 };
// mockListsService.create의 동작 설정
// 이 메서드 호출 시 result값을 반환하도록 설정 >> 테스트 성공 결과 미리 지정
mockListsService.create.mockResolvedValue(result);
// create 메서드 호출, createListDto가 인자로 전달, await으로 비동기 작업 완료까지 대기
const response = await controller.create(createListDto);
// 검증 단계
// 1. service.create 가 createListDto 인자로 호출되었는지 검증
// 2. response가 예상 결과와 동일한지 확인
expect(service.create).toHaveBeenCalledWith(createListDto);
expect(response).toEqual(result);
});
});
2. update 메서드
// src/lists/lists.controller.ts
...
@Patch(':id')
async update(@Param('id') id: string, @Body() updateListDto: UpdateListDto) {
return this.listsService.update(+id, updateListDto);
}
...
URL의 파라미터를 id로 받아 해당하는 리스트를 업데이트
내부에서 문자열로 전달된 파라미터를 숫자로 변환하여 서비스의 update 메서드에 전달
describe('update', () => {
it('리스트 업데이트 테스트', async () => {
// 테스트 데이터 설정
const id = '1';
const updateListDto: UpdateListDto = { title: '수정된 리스트 제목' };
// result : 예상 결과 값
const result = { id: 1, boardId: 1, title: '수정된 리스트 제목', position: 1 };
// update 메서드 호출 시 result값을 반환하도록 설정 >> 테스트 성공 결과 미리 지정
mockListsService.update.mockResolvedValue(result);
// update 메서드 호출, id, updateListDto 인자로 전달, await으로 비동기 작업 완료까지 대기
const response = await controller.update(id, updateListDto);
// 검증 단계
// 1. service.update 가 1, updateListDto 인자로 호출되었는지 검증
// 2. response가 예상 결과와 동일한지 확인
expect(service.update).toHaveBeenCalledWith(1, updateListDto);
expect(response).toEqual(result);
});
});
3. remove 메서드
// src/lists/lists.controller.ts
...
@Delete(':id')
async remove(@Param('id') id: string) {
return this.listsService.remove(+id);
}
...
URL의 파라미터를 id로 받아 해당하는 리스트를 삭제
내부에서 문자열로 전달된 파라미터를 숫자로 변환하여 서비스의 remove 메서드에 전달
describe('remove', () => {
it('리스트 삭제 테스트', async () => {
// 테스트 데이터 설정
const id = '1';
// remove 메서드 호출 시 undefined 반환하도록 설정 >> 삭제 경우이므로
mockListsService.remove.mockResolvedValue(undefined);
// remove 메서드 호출, id 인자로 전달, await으로 비동기 작업 완료까지 대기
const response = await controller.remove(id);
// 검증 단계
// 1. service.remove 가 1 인자로 호출되었는지 검증
// 2. response가 undefined인지 검증
expect(service.remove).toHaveBeenCalledWith(1);
expect(response).toBeUndefined();
});
});
4. updatePositions 메서드
// src/lists/lists.controller.ts
...
@Patch() // 명확한 엔드포인트 지정 권장
async updatePositions(
@Body() updateListPositionsDto: UpdateListPositionsDto,
): Promise<{ message: string }> {
await this.listsService.updatePositions(updateListPositionsDto);
return { message: '리스트 위치가 성공적으로 업데이트되었습니다.' };
}
...
body에 전달된 updateListPositionsDto를 이용해 여러 리스트의 위치를 업데이트
describe('updatePositions', () => {
it('리스트 포지션 업데이트 테스트', async () => {
// 테스트 데이터 설정
const updateListPositionsDto: UpdateListPositionsDto = {
boardId: 1,
lists: [
{ id: 1, position: 1 },
{ id: 2, position: 2 },
{ id: 3, position: 3 },
],
};
// updatePositions 메서드 호출 시 undefined 반환하도록 설정 >> 성공한 경우
mockListsService.updatePositions.mockResolvedValue(undefined);
// updatePositions 메서드 호출, updateListPositionsDto 인자로 전달,
// await으로 비동기 작업 완료까지 대기
const response = await controller.updatePositions(updateListPositionsDto);
// 검증 단계
// 1. updatePositions 가 updateListPositionsDto 인자로 호출되었는지 검증
// 2. response가 예상 결과와 동일한지 확인
expect(service.updatePositions).toHaveBeenCalledWith(updateListPositionsDto);
expect(response).toEqual({
message: '리스트 위치가 성공적으로 업데이트되었습니다.',
});
});
});
5. 최종 lists.controller.spec.ts
// src/lists/lists.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { ListsController } from './lists.controller';
import { ListsService } from './lists.service';
import { CreateListDto } from './dto/create-list.dto';
import { UpdateListDto } from './dto/update-list.dto';
import { UpdateListPositionsDto } from './dto/update-list-positions.dto';
describe('ListsController', () => {
let controller: ListsController;
let service: ListsService;
const mockListsService = {
create: jest.fn(),
update: jest.fn(),
remove: jest.fn(),
updatePositions: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ListsController],
providers: [
{
provide: ListsService,
useValue: mockListsService,
},
],
}).compile();
controller = module.get<ListsController>(ListsController);
service = module.get<ListsService>(ListsService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('create', () => {
it('리스트 생성 테스트', async () => {
const createListDto: CreateListDto = {
boardId: 1,
title: 'New List',
};
const result = { id: 1, boardId: 1, title: 'New List', position: 1 };
mockListsService.create.mockResolvedValue(result);
const response = await controller.create(createListDto);
expect(service.create).toHaveBeenCalledWith(createListDto);
expect(response).toEqual(result);
});
});
describe('update', () => {
it('리스트 업데이트 테스트', async () => {
const id = '1';
const updateListDto: UpdateListDto = { title: '수정된 리스트 제목' };
const result = { id: 1, boardId: 1, title: '수정된 리스트 제목', position: 1 };
mockListsService.update.mockResolvedValue(result);
const response = await controller.update(id, updateListDto);
expect(service.update).toHaveBeenCalledWith(1, updateListDto);
expect(response).toEqual(result);
});
});
describe('remove', () => {
it('리스트 삭제 테스트', async () => {
const id = '1';
mockListsService.remove.mockResolvedValue(undefined);
const response = await controller.remove(id);
expect(service.remove).toHaveBeenCalledWith(1);
expect(response).toBeUndefined();
});
});
describe('updatePositions', () => {
it('리스트 포지션 업데이트 테스트', async () => {
const updateListPositionsDto: UpdateListPositionsDto = {
boardId: 1,
lists: [
{ id: 1, position: 1 },
{ id: 2, position: 2 },
{ id: 3, position: 3 },
],
};
mockListsService.updatePositions.mockResolvedValue(undefined);
const response = await controller.updatePositions(updateListPositionsDto);
expect(service.updatePositions).toHaveBeenCalledWith(updateListPositionsDto);
expect(response).toEqual({
message: '리스트 위치가 성공적으로 업데이트되었습니다.',
});
});
});
});
'TIL' 카테고리의 다른 글
내일배움캠프 14주차 금요일 TIL (0) | 2025.01.31 |
---|---|
내일배움캠프 13주차 금요일 TIL (0) | 2025.01.24 |
내일배움캠프 13주차 목요일 TIL (0) | 2025.01.23 |
내일배움캠프 13주차 화요일 TIL (1) | 2025.01.21 |
내일배움캠프 12주차 금요일 TIL (0) | 2025.01.17 |