(최종 프로젝트 진행중)
오늘은 팀 회의와 튜터님과의 멘토링을 통해 팀원 각자가 맡을 파트를 기술(페이지네이션, 이미지 업로드 등) 기준으로 재할당하고 범위를 구체적으로 한정지었다. 부담을 조금 덜 수 있어 좋았다.
어제에 이어 여러 레퍼런스의 도움을 얻어 마침내 테스트 코드 감을 좀 잡게 되었다. 남은 것은 팀원들이 봤을 때 쉽게 따라할 수 있도록 코드를 다듬고 공유하는 것.
그리고 오늘 저녁엔 ‘동네(위치) 인증 기반 서비스’를 어떻게 얼마나 제공할 수 있을까에 대해 혼자 조사를 해 보았는데 몇 가지 자료를 찾을 수 있었다. (조사하며 마구 필기해 놓은 것은 너무 날림이라 오늘 TIL에 포함하지 않았다.) 이게 핵심 기능이었는데 다행히도 서툴게나마 구현할 수 있을 것 같다.
이어서 할 일:
- 카카오 우편 서비스 API가 좌표 ↔ 주소 로 바로바로 전환해주는지. 특히 좌표를 주소로.
→ 유저에게 동네 인증하게 하는 흐름 짜보기.
- queryBuilder 문서 보던중. 두 번 조인과 그 때 select하는 법, (https://typeorm.io/select-query-builder#join-without-selection)
- 그렇게 가져온 ‘정재된 결과물’을 partial 타입으로 넘겨서 테스트 할 것인지… 그렇다면 어떻게.. …?
- 테스트 코드 강의 노트와 유튜브 보던 중
- Jest 공식문서
- 그 밖에 (도움이 필요하면 조금 더 읽기를 진행해볼 것들)
기초부터 설명 but 너무 장황 (https://www.tomray.dev/nestjs-unit-testing )
try-catch 가 적절한 에러 핸들링 방법이라고 알려준 (https://blog.bitsrc.io/successfully-throwing-async-errors-with-the-jest-testing-library-fda17261733a)
그보다 toThrow assertions가 베스트 에러 핸들링이라는 (https://blog.codeleak.pl/2021/11/testing-exceptions-in-javascript.html)
toThrow가 에러 타입 자체도 받고 에러베세지(문자열)도 인자로 받는다고 알려준 (https://www.webtips.dev/webtips/jest/expect-error-in-jest)
※ 이하는 스스로 공부하며 적어둔 노트이며 불확실한 내용이 있을 수 있습니다. 학습용으로 적합하지 않음을 유념해주세요. ※
[Jest + Nest.js] 단위 테스트할 때 자꾸 실제 주입된 repository를 참조하던 문제 ✔️
[Jest + Nest.js] 단위 테스트할 때 자꾸 실제 주입된 repository를 참조하던 문제 ✔️
- 왜 mockRepository 만들고 prodiver에도 덮어 씌웠는데, ‘원래의 repository’에 해당 메소드가 없다고 빨간줄이 뜨는거지???
분명 service를 테스트할 때 mock repository를 덮어씌워 주입해줬다고 생각했는데 빨간줄이 자꾸 실제 커스텀 repository를 참조하면서 ‘이런 메소드가 repository에 정의되어 있지 않다’고 하는 문제가 있었다. Service 테스트 코드도 작성해보고 controller 테스트 코드도 작성해보고, 소스 코드도 여기저기 바꿔보며 시도하는 중에 계속 비슷한 맥락의 에러가 나는 것이다. Nest.js 기반 테스트 코드 작성은 처음이었지만 이게 잘못되었다는 생각이 구체화되어 가던 도중, 레퍼런스를 찾아다니며 이것저것 시도해보다가 알게 되었다. 제대로 mock provider를 설정해주는 방법을. 그리고 Jest 공식 문서에서 testing에 관해 알려줄 때 e2e 테스트 파트에서 나온 .overrideProvider()
나 계속 마주치게 되는 .useValue/Class/Factory()
의 의미까지.
- 주입되는 provider가 mock으로 독립되지 못하고 실제 provider를 참조하게 되는 잘못된 예시:
// src/messages/messages.service.spec.ts
const mockMessagesRepository = () => {
return {
getMessageById: jest.fn(),
getMessages: jest.fn(),
getReceivedMessages: jest.fn(),
getSentMessages: jest.fn(),
createMessage: jest.fn(),
deleteMessage: jest.fn(),
};
};
describe('MessagesService', () => {
let service: MessagesService;
let repository: jest.Mocked<MessagesRepository>;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
MessagesService,
{
provide: MessagesRepository,
useFactory: mockMessagesRepository,
},
// {
// provide: getRepositoryToken(Message),
// useValue: {},
// },
],
}).compile();
service = module.get<MessagesService>(MessagesService);
repository = module.get(MessagesRepository);
});
})
⇒ 커스텀 MessagesRepository를 mockMessagesRepository로 덮어씌웠다고 생각했지만 제대로 되지 않았던 코드. 저 mockMessagesRepository에 아무리 getReceivedMessages와 getSentMessages를 만들어 뒀어도 실제 MessagesRepository에 저 메소드들을 정의하기까지 계속해서 참조 오류를 발생시켰었다. (주석 처리된 부분은 실험해보지 않았다. )
- 제대로 mock provider를 주입한 예시:
// src/messages/messages.controller.spec.ts
describe('MessagesController', () => {
let controller: MessagesController;
const mockMessagesService = {
createMessage: jest.fn((dto) => {
return {
id: 1,
...dto,
};
}),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [MessagesController],
providers: [MessagesService],
})
.overrideProvider(MessagesService)
.useValue(mockMessagesService)
.compile();
controller = module.get<MessagesController>(MessagesController);
});
[Jest] --watch 와 --watchAll의 차이
[Jest] --watch 와 --watchAll의 차이
"test:watch": "jest --watch",
"test:watch": "jest --watchAll --verbose",
예를 들어 하나의 테스트 파일(messages.controller.spec.ts)을 실행하고 있다고 할 때, --watch는 그 파일이 수정되었을 때만 감지하여 업데이트한다(적어도 다른 messages.service.ts나 messages.repository.ts는 감지하지 않음). --watchAll는 그 외에 연관된 파일을 수정해야 할 때도 감지해준다. 정말… 이걸 몰라서 “제대로 수정했는데 왜 계속 같은 에러와 테스트 실패를 띄우는 거지..?” 이러고 한참 삽질했다.
Nest 이 사람들
왜 테스트 기본값을 “jest --watch”로 해놓은 것인가. 프레임워크 기본 틀이 controller, provider들의 계층 구조라면 당연히 --watchAll로 옆 파일까지 변경 추적이 가능하도록 해놨어야지..!
이러고 분노하고 있었는데, 위의 에러를 해결하는 과정을 거치고 또 자세히 알아보니 --watch가 맞다는 걸 알게 되었다.
--watch
Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the
--watchAll
option instead.
‘related files’란 이름이 같은 걸 기준으로 구분하는 것이겠지. 내가 위의 단위 테스트 실험 과정에서 mocking을 제대로 해내서 실제 하위 계층 provider가 참조되는 일이 없었다면 ‘(다른) 파일을 수정했는데 왜 재실행하는 test에 반영이 안 되는 것인가’ 하는 문제가 발생하지 않았을 것이다.
결론:
- 단위 테스트를 의도한 Nest.js의 “test:watch” 명령에는 (원래대로)
--watchAll
이 아니라--watch
가 맞다.
- 하나의 테스트 파일만을
--watch
하는 상태에서는, 다른 이름의 소스 파일을 수정하고 저장해도 그 결과가 테스트에 반영되지 않는다. 이전에 참조 오류가 나던 상태라면 계속해서 참조 오류가 날 것이다.
- ⇒ 하나의 테스트 파일을 타겟으로 재실행 하며 테스트 하려면: 타겟 테스트 파일만을 실행하면서
--watchAll
로 추적해주거나, 타겟 테스트 파일을--watch
로 실행하면서 다른 어떤 소스파일에도 의존성이 번지지 않도록 mocking을 제대로 해주는 것. 두 가지 방법이 있다.
(참조: https://jestjs.io/docs/cli#--watch)
Uploaded by N2T