node-mocks-http 란?
핵심만 말하자면 Express.js, Next.js 애플리케이션에서 라우팅 함수를 테스트 하기 위해 http 객체(request, response)를 생성할때 사용한다. 실제로 restAPI를 통해 http 통신을 할때 request 와 response 객체를 정말 많이 마주하지만 이를 테스트하기 위해 http 객체를 모킹하는 방법이 번거롭고 까다롭다. 하지만 node-mocks-http를 사용하여 간단하게 express/next.js의 request, response mock 객체를 생성할수있다. 글로 설명하는 하는 것 보다 아래 설치 및 사용법에서 예시 코드와 함께 정리해보려 한다.
node-mocks-http 설치 및 사용법
1. node-mocks-http 설치
npm i node-mocks-http --save-dev
우선 node-mocks 를위 명령어를 프로젝트 경로에서 실행시켜 설치할 수 있다.
2. 테스트 코드 작성
아래의 예시 코드는 mongoose 모듈을 활용하여 express.js 어플리케이션에서 mongo DB에 데이터를 적재하는 과정을 테스트하기 위한 테스트 코드의 일부이다.
/* /test/unit/products.test.js */
const productModel = require('../../models/Product');
const productController = require('../../controller/products');
const httpMocks = require('node-mocks-http');
let req, res, next;
// 다른 테스트 케이스에서도 사용할 수 있도록 불필요한 반복을 줄이기 위해 beforeEach 사용
beforeEach(() => {
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn();
});
describe('Product Controller Create', () => {
beforeEach(() => {
req.body = newProduct;
});
//expect와 matcher를 통해서 DB에 데이터가 되는 부분 테스트
it('should call ProductModel.create', async () => {
await productController.createProduct(req, res, next);
expect(productModel.create).toBeCalledWith(newProduct);
});
});
위와 같이 httpMocks 를 가져와서 선언한 뒤 createRequest와 createResponse 함수를 사용하여 request 객체와 response 객체를 생성한다. 이 후 테스트 코드를 작성하고 createProduct에 req, res 를 인자로 넣어준 뒤 expect와 matcher를 사용하여productModel.create 함수 호출시 newProduct 객체가 같이 호출됐는지 테스트를 진행할 수 있다.
단, 테스트 코드 작성후 이를 기반으로 실제코드 작성 후 테스트를 진행해야 한다.
3. 실제 코드 작성
테스트 코드를 작성했다면 테스트를 통과하기 위한 실제 코드를 아래와 같이 작성한다.
/* /controller/products.js */
const productModel = require('../models/Product');
exports.createProduct = async (req, res, next) => {
try {
const createdProduct = await productModel.create(req.body);
res.status(201).json(createdProduct);
} catch (error) {
next(error);
}
};
테스트 코드에 작성된 대로 productModel.create 호출시 newProduct가 인자로 함께 전달될 수 있도록 request 객체로 전달될 req인자의 body 값을 같이 넣어준다.
▪︎ 참고
node-mocks-http의 response객체는 테스트 검증을 위한 아래와 같은 여러가지 함수를 제공하는데 각 경우에 맞게 유용하게 사용할 수 있다.
//res.status(201).send()에서 send()나 json()과 같이 추가적인 결과값이 전달되고 있는지 확인할 수 있다. 전달된다면 true
expect(res._isEndCalled()).toBeTruthy();
//res.status(201).json(createdProduct)과 같이 response값이 JSON 형식인지 true/false
expect(res._isJSON()).toBeTruthy();
//response에 담긴 JSON 타입의 결과 데이터를 확인할 수 있다.
expect(res._getJSONData()).toStrictEqual([JSONData]);
//response 상태코드 값을 확인할 수 있다.
expect(res.statusCode).toBe(201);
//response 인코딩 형식이 UTF-8 인지 true/fasle
expect(res._isUTF8()).toBeTruthy();