지난번에 이어서 Read, Update, Delete 테스트를 작성해보자.
이전 글 ➡️ Jest를 사용해보자(with Express) #1
...
let req: MockRequest<any>;
let res: MockResponse<any>;
let next: NextFunction;
beforeEach(() => {
req = httpMocks.createRequest();
res = httpMocks.createResponse();
next = jest.fn<any>();
});
productModel.find = jest.fn<any>();
describe('Product Controller Get', () => {
it('should have a getProducts', () => {
expect(typeof productController.getProducts).toBe('function');
});
it('should call ProductModel.find({})', async () => {
await productController.getProducts(req, res, next);
expect(productModel.find).toBeCalledWith({});
});
it('should return 200 response', async () => {
await productController.getProducts(req, res, next);
expect(res.statusCode).toBe(200);
expect(res._isEndCalled).toBeTruthy();
});
it('should return json body in response', async () => {
(productModel.find as jest.Mock).mockReturnValue(allProducts);
await productController.getProducts(req, res, next);
expect(res._getJsonData()).toStrictEqual(allProducts);
});
});
_isEndCalled
: node-mocks-http에서 제공하는 메서드로, send()나 json()과 같이 추가적인 결과값이 전달되고 있는지 확인할 수 있음_getJSONData()
: 전달한 JSON의 결과값을 참조할 수 있음toBeCalledWith(param)
: 해당 파라미터를 가진 함수가 한 번이라도 호출된 적 있으면 통과...
// 단위 테스트에서 id는 실제 존재하지 않는 것 넣어도 된다
// 실제로 mongoDB는 잘 작동한다고 가정하고 테스트하는 것이기 때문
const productId = '<collection_id>';
productModel.findById = jest.fn<any>();
...
describe('Product Controller GetById', () => {
it('should have getProductById', () => {
expect(typeof productController.getProductById).toBe('function');
});
it('should call ProductModel.findById', async () => {
req.params.productId = productId;
await productController.getProductById(req, res, next);
expect(productModel.findById).toBeCalledWith(productId);
});
it('should return json body and response code 200', async () => {
(productModel.findById as jest.Mock).mockReturnValue(newProduct);
await productController.getProductById(req, res, next);
expect(res.statusCode).toBe(200);
expect(res._getJSONData()).toStrictEqual(newProduct);
expect(res._isEndCalled()).toBeTruthy();
});
it('should return 404 when item does not exist', async () => {
(productModel.findById as jest.Mock).mockReturnValue(null);
await productController.getProductById(req, res, next);
expect(res.statusCode).toBe(404);
expect(res._isEndCalled()).toBeTruthy();
});
it('should handle errors', async () => {
const errorMessage = { message: 'error' };
const rejectedPromise = Promise.reject(errorMessage);
(Product.findById as jest.Mock).mockReturnValue(rejectedPromise);
await productController.getProductById(req, res, next);
expect(next).toBeCalledWith(errorMessage);
});
});
it('GET /api/products/:productId', async () => {
//통합 테스트에서는 실제 mongoDB를 통해서 요청 이루어지기 때문에 존재하는 id 넣어야 함
const response = await request(server).get(`/api/products/${firstProduct._id}`);
expect(response.statusCode).toBe(200);
expect(response.body.name).toBe(firstProduct.name);
expect(response.body.description).toBe(firstProduct.description);
});
it('GET id does not exist /api/products/:productId', async () => {
//productId를 아무거나 입력할 시에는 mongoDB가 유효하지 않은 것을 인지하고 next로 넘어가 500을 줌
const response = await request(server).get(`/api/products/63501a235fa7608887128981`);
expect(response.statusCode).toBe(404);
});
...
productModel.findByIdAndUpdate = jest.fn<any>();
...
describe('Product Controller Update', () => {
it('should have an update function', () => {
expect(typeof productController.updateProduct).toBe('function');
});
it('should call ProductModel.findByIdAndUpdate', async () => {
req.params.productId = productId;
req.body = updatedProduct;
await productController.updateProduct(req, res, next);
expect(productModel.findByIdAndUpdate).toBeCalledWith(productId, updatedProduct, { new: true });
});
it('should return json body and response code 200', async () => {
req.params.productId = productId;
req.body = updatedProduct;
(productModel.findByIdAndUpdate as jest.Mock).mockReturnValue(updatedProduct);
await productController.updateProduct(req, res, next);
expect(res.statusCode).toBe(200);
expect(res._getJSONData()).toStrictEqual(updatedProduct);
expect(res._isEndCalled()).toBeTruthy();
});
it('should return 404 when item does not exist', async () => {
(productModel.findByIdAndUpdate as jest.Mock).mockReturnValue(null);
await productController.updateProduct(req, res, next);
expect(res.statusCode).toBe(404);
expect(res._isEndCalled()).toBeTruthy();
});
it('should handle errors', async () => {
const errorMessage = { message: 'error' };
const rejectedPromise = Promise.reject(errorMessage);
(productModel.findByIdAndUpdate as jest.Mock).mockReturnValue(rejectedPromise);
await productController.updateProduct(req, res, next);
expect(next).toBeCalledWith(errorMessage);
});
});
뭔가 정확하게 안다기 보다는 그냥 계속 똑같은 걸 하다보니까 익숙해지는 것 같다.
일단 정리해두고 더 확실하게 공부해보자.
(특히 언제 파라미터 넘겨주고 이러는지 제대로 모르겟슴)