Jest를 사용해보자(with Express) #2

cdwde·2022년 10월 24일
0
post-thumbnail

지난번에 이어서 Read, Update, Delete 테스트를 작성해보자.
이전 글 ➡️ Jest를 사용해보자(with Express) #1

✅ Read 단위테스트 작성

상품 전체 조회 관련

  • product.unit.test
...

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): 해당 파라미터를 가진 함수가 한 번이라도 호출된 적 있으면 통과

상품 상세 조회 관련

  • product.unit.test
...
// 단위 테스트에서 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);
  });
});

✅ Read 통합테스트 작성

  • products.int.test
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);
});

✅ Update 단위테스트 작성

...
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);
  });
});

뭔가 정확하게 안다기 보다는 그냥 계속 똑같은 걸 하다보니까 익숙해지는 것 같다.
일단 정리해두고 더 확실하게 공부해보자.
(특히 언제 파라미터 넘겨주고 이러는지 제대로 모르겟슴)

참고
jest mock 함수
따라하며 배우는 TDD

0개의 댓글