여러가지 자료를 보면서 test코드를 작성하던 나는 위와같은 타입에러를 마주하였다.
나는 컨트롤러 부분의 test코드를 작성하면서 req와 res를 mocking해서 내부에 있는 함수들도 직접 작성하는 쪽으로 했는데,
일단 테스트코드를 만들고 있던 진짜 코드의 로직은 아래와 같았다.
@UseGuards(RefreshTokenGuard)
@HttpCode(HttpStatus.CREATED)
@Post('/refresh-token')
async checkRefresh(
@Req() req: Request,
@Res() res: Response,
): Promise<Response> {
const user: any = req.user;
const payload: Payload = { id: user.id, nickname: user.nickname };
const newJwt = await this.authService.createAccessToken(payload);
res.setHeader('Authorizetion', 'Bearer ' + newJwt);
return res.json({ msg: '토큰 재발급' });
}
그리고 테스트 코드는 아래의 형식이었다.
const mockReq = {
user: { id: expect.any(Number), nickname: expect.any(String) },
} as unknown as Request;
const mockRes = {
json: jest.fn(),
setHeader: jest.fn(),
cookie: jest.fn(),
} as unknown as Response;
//-------
it('엑세스 토큰 재발급 성공 케이스', async () => {
mockAuthService.createAccessToken = jest.fn(() => {
return 'access';
});
await authController.checkRefresh(mockReq, mockRes);
expect(mockAuthService.createAccessToken).toBeCalledWith(mockReq.user);
expect(mockRes.setHeader).toBeCalledWith('Authorizetion', 'Bearer access');
expect(mockRes.json).toBeCalledWith({ msg: '토큰 재발급' });
});
하지만 controller의 로직 실행도 전에 이미 인자에서 타입에러가 반출되었다.
정확히는 await authController.checkRefresh(mockReq, mockRes);
의 mockReq
와
expect(mockAuthService.createAccessToken).toBeCalledWith(mockReq.user);
의 mockReq.user
에서 에러를 뿜었다.
에러내용은 아래의 두 가지였다.
'Request' 형식의 인수는 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' 형식의 매개 변수에 할당될 수 없습니다.
'Request' 형식에 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' 형식의 get, header, accepts, acceptsCharsets 외 83개 속성이 없습니다.
'Request' 형식에 'user' 속성이 없습니다.
내용을 읽어보면 Request' 형식의 인수는
즉 mockReq는 Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>' 형식의 매개 변수
async checkRefresh(@Req() req: Request,@Res() res: Response)
에서의 req에 할당할 수 없다고 한다.
또한 get, header, accepts, acceptsCharsets 외 83개 속성이 없습니다.
는 그럼 내가 moking을 86개 속성에 대해서 다 해줘야한다는 건가? 라는 생각을 들게 만들었다.
Request에 대한 mocking이 잘못된것이라고 생각되어, 객체내의 일부만 mocking하는 방법을 알아보았는데 다음과 같이 타입을 쓰는 방법이었다.
const mockReq: Partial<Request> = {
user: { id: expect.any(Number), nickname: expect.any(String) },
}
const mockRes: Partial<Response> = {
json: jest.fn(),
setHeader: jest.fn(),
cookie: jest.fn(),
}
Partial<...>
...에 타입을 넣어 내부의 일부 내용에 대해서만 타입을 재정의 해주는 방식인것 같았다.
하지만 코드를 변경하여도 type error는 여전했다. 그러다가 생각해 보니 왜 mockRes에서는 type error가 발생하지 않는거지? 라는생각이 들었다. 그래서 마우스를 호버해봤다.
mockRes는 Response<ant, Record<string,any>>
타입을 가지고 있었는데
mockReq는 Request
타입 즉 Request를 직접적으로 타입으로 지정하고 mocking이 적용되지 않고 있었다.
왜지? 분명 똑같은 절차를 통해서 mocking을 진행했는데 하나는 똑바로되고 하나는 안 된 걸까?
혹시나.. 해서 임포트 구문쪽을 살펴보았다.
에라이.. express에서 Response는 가지고 오는데 Request는 가지고 오고있지 않았다. 평소 자동 임포트를 습관적으로 쓰다보니 이러한 에러가 발생한 것이었다. 지금 내가 참조하고 잇는 Request는 express에서 가져온게 아닌 전혀다른 Interface인 Request를 가지고 오고 있었다.
import 구문의 express부분에 Request를 추가해 주니 간단하게 해결되었다.
이 정도 에러를 가지고 왜 그렇게 해메고 있었는지.. 첫 NestJS 테스트 코드를 작성하면서 이전 UUID와 같이 Mocking이 안되는 경우 때문에 괜히 골머리 아프게 돌려 생각했던게 화근이었던 것 같다. 이 에러는 이 에러고, 저 에러는 저 에러다. 해깔리지 말자.