-
nodejs 환경에서 jest를 이용해 미들웨어를 test해보자(ts)카테고리 없음 2021. 10. 17. 15:12
npm i -D jest ts-jest @types/jest
서비스가 커져감에 따라서 이제 손으로 테스트를 하는데는 한계가 찾아왔다.
수많은 라우터와 미들웨어, 함수들이 존재해서 버전업이 될때마다 하나하나 확인을 하기 힘들었다.
그래서 그동안 서버응답여부만 체크하던 jest를 더 확장해서 모든 기능들을 테스트하기로 결정했다.
우선 jest를 설치하자.
필요한 패키지는 다음과 같다.
npm i -D jest ts-jest @types/jest
그리고 jest.config.js 파일을 생성한다.
/** @type {import('@ts-jest/dist/types').InitialOptionsTsJest} */ module.exports = { preset: 'ts-jest', testEnvironment: 'node', moduleNameMapper: { '^@src/(.*)$': '<rootDir>/src/$1', '^@db$': '<rootDir>/src/db.ts', '^@middlewares/(.*)$': '<rootDir>/src/middlewares/$1', '^@routers/(.*)$': '<rootDir>/src/routers/$1', '^@services/(.*)$': '<rootDir>/src/services/$1', }, };
preset과 testEnvironment는 그대로, moduleNameMapper는 본인의 프로젝트에 따라서 적절하게 작성하면 된다.
그럼 로그인 여부를 판단하는 미들웨어인 isLoggedIn, isNotLoggedIn 미들웨어를 테스트하는 코드를 작성해보자.
일단, 우리의 미들웨어는 다음처럼 생겼다.
export const isLoggedIn: expressMiddleware = (req: Request, res: Response, next: NextFunction) => { if (req.isAuthenticated()) { return next(); } return res.status(403).json({ info: 'Login Required', }); }; export const isNotLoggedIn: expressMiddleware = (req: Request, res: Response, next: NextFunction) => { if (!req.isAuthenticated()) { return next(); } return res.status(403).json({ info: 'Account is already logged in', }); };
이제 test폴더를 만들고, 그 안에 middleware 폴더를 만든 뒤 파일을 하나 생성해준다.
우리의 경우 파일명을 auth.test.ts로 정했다. 위의 미들웨어가 src/middleware/auth.ts 경로에 존재하기 때문이다.
우선 완성된 코드를 보자.
import { mockRequest, mockResponse } from 'jest-mock-req-res'; import { isLoggedIn, isNotLoggedIn } from '../../src/middlewares/auth'; describe('isLoggedIn', () => { const res: MockResponse = mockResponse({}); const next: () => void = jest.fn(); test('로그인이 되어있으면 isLoggedIn이 next를 호출해야 함', () => { const req: MockRequest = mockRequest({ isAuthenticated: jest.fn(() => true), }); isLoggedIn(req, res, next); expect(next).toBeCalledTimes(1); }); test('로그인이 되어있지 않으면 isLoggedIn이 403에러를 응답해야 함', () => { const req: MockRequest = mockRequest({ isAuthenticated: jest.fn(() => false), }); isLoggedIn(req, res, next); expect(res.status).toBeCalledWith(403); expect(res.json).toBeCalledWith({ info: 'Login Required', }); }); });
describe는 test를 그룹화해주는 역할이고, test 함수가 실행되면서 expect와 일치하는지 확인을 하게 된다.
expect뒤의 메서드들은 jest 공식문서에 잘 나와있다.
res, req의 mock객체가 필요한데, 이는 다음 패키지로 해결 가능하다.
npm i -D jest-mock-req-res
우리는 ts를 사용중이니, 이 mock객체의 타입도 알아야한다.
mockRequest와 mockResponse의 정의를 찾아가보면, MockRequest와 MockResponse라는 인터페이스가 선언된 것을 확인할 수 있다. 그런데 export가 되어있지 않다..! 따라서 해당 내용을 복사해서 따로 타입 정의를 해주어야 한다.
src/@types/req-res.d.ts 파일을 생성하고 해당하는 인터페이스들을 복사해서 작성해준다.
/// <reference types="jest" /> import { Response, Request } from 'express'; declare global{ interface MockResponse extends Response { append: jest.Mock; attachment: jest.Mock; clearCookie: jest.Mock; contentType: jest.Mock; cookie: jest.Mock; download: jest.Mock; end: jest.Mock; format: jest.Mock; get: jest.Mock; header: jest.Mock; json: jest.Mock; jsonp: jest.Mock; links: jest.Mock; location: jest.Mock; redirect: jest.Mock; render: jest.Mock; send: jest.Mock; sendFile: jest.Mock; sendStatus: jest.Mock; set: jest.Mock; status: jest.Mock; type: jest.Mock; vary: jest.Mock; } interface MockRequest extends Request { accepts: jest.Mock; acceptsCharsets: jest.Mock; acceptsEncodings: jest.Mock; acceptsLanguages: jest.Mock; get: jest.Mock; header: jest.Mock; is: jest.Mock; range: jest.Mock; } }
global로 선언되었으므로 해당 타입을 사용 가능하다.
res, req를 따로 인터페이스를 선언해서 사용하는 방법이 많이 나오던데, 해당 방법으로 하면 계속 타입이 옳지 않다고 에러를 뱉어서 jest-mock-req-res를 import했고, any타입을 없애기 위해서 인터페이스들을 추가로 선언해주었다.
다음에는 테이블을 mockup해서 라우터들을 테스트하는 포스트로 돌아오겠다.