Node.js TDD 단위테스트, 통합테스트

TDD Study 용 기본 Module

더보기

Production 환경

  1. Express
  2. Mongoose
npm install express mongoose --save

 

Development 환경

  1. Jest
  2. Node-mocks-http
  3. supertest
npm install jest supertest node-mocks-http --save-dev

 

Extensions

  1. JestRunner

단위(Unit)테스트

더보기

정의

개발자가 작성한 모듈 단위의 코드를 테스트하는 것. 소스 코드의 개별 단위 를 테스트 하여 준비 여부를 확인.

 

조건

  1. 독립적이어야 한다. (어떤 테스트도 다른 테스트에 의존하면 안됨)
  2. 격리 되어져야 한다. 주입을 받거나 model을 받아야 하는 경우 mockup으로 처리 하여야 한다.

 

목적

  1. 프로그램이 크고 메모리가 많이 들면 실행 해보는 것이 쉽지 않음.
  2. 종속성에 있는 다른 클래스의 버그를 방지 하기 위함.

Jest

더보기

설명

FaceBook에 의해서 만들어진 테스팅 프레임 워크이다.

최소한의 설정으로 동작하며 Test Case를 만들어서 확인하면 된다.

 

시작하기

이미 모듈 의존성을 추가 하였음.

server.js 부분에 있는 script 를 아래와 같이 좀만 수정하면 됨.

"test" : "jest"

Jest가 Test 파일을 찾는 방법

  1. {filename}.test.js
  2. {filename}.spec.js
  3. All files inside “tests” folders

 

용어

  1. describe : 여러 관련 테스트를 그룹화하는 블록.
  2. it, test : 개별 테스트를 수행하는 곳.
  3. expect : 값을 테스트 할 때마다 예상 되는 값을 입력, 혼자 사용되지 않고 matcher와 함께 사용이 됨.
  4. matcher : 다른 방법으로 값을 테스트 하는 방식으로 사용.
describe('calculatation', () => {
    test('two plus two is four', () => {
        expect(2 + 2).toBe(4);
    });

    test('two plus two is not five', () => {
        expect(2 + 2).not.toBe(5);
    });
});

Mock

더보기

설명

Mock은 가짜라는 의미로 사용되는 함수로 실제로 데이터가 입력 되어져야 하는 부분을 Mock이 대체되어서 진행 됩니다.

 

목적

독립적인 테스트 상황을 만들기 위해 사용됨. 다른 테스트에 의존을 하게 되면 실제로 문제가 생기면 어디가 문제인지 파악이 어려움.

 

사용

jest.fn() 를 이용해서 가짜 함수를 생성. 생성한 가짜 함수는 이 함수에 어떤 일들이 발생했는지, 다른 코드들에 의해서 어떻게 호출 되는 지를 기억함.


node-mocks-http

더보기

Express 라우팅 기능을 테스트하는 모듈로 requestresponse 를 만들어서 테스트 코드에 제공을 할 수있다.

let req = httpMocks.createRequest();
let res = httpMocks.createResponse();

mockReturnValue : 해당 메소드에 요청을 했을 때 어떠한 값을 반환하게 할 때 사용되는 메소드

productModel.create.mockReturnValue(newProduct);

모듈에서 자체적으로 테스트를 위한 메소드를 제공하고 있다. 자세한 내용은 아래 링크를 보고 확인을 하면 됨.

 

node-mocks-http

Mock 'http' objects for testing Express routing functions. Latest version: 1.12.2, last published: 4 months ago. Start using node-mocks-http in your project by running `npm i node-mocks-http`. There are 141 other projects in the npm registry using node-moc

www.npmjs.com


matcher 메소드

더보기

matcher 는 테스트코드를 검증하기 위한 메소드를 지원하고 있다. 그렇기 때문에 상황에 따라 맞는 메소드를 사용하면 됨.

toBe('${type}') : 해당 기능은 이것은 ~이다 라는 검증을 하는데 사용이 된다.

it("should have a createProduct function", () => {
      expect(typeof productController.createProduct).toBe("function");
   });

toBeCalled() : 타켓 메소드 호출된다라는 것을 검증하는데 사용됨.

it("should call ProductModel.create", () => {
      productController.createProduct();
      expect(productModel.create).toBeCalled();
   });

toBeCalledWith() : 메소드가 호출이 되면서 어떠한 값들도 같이 호출 된다는 것을 검증하는 메소드.

productController.createProduct(req, res, next);
      expect(productModel.create).toBeCalledWith(newProduct);

toHaveBeenCalledWith : 메소드가 호출이 되면서 어떠한 값들도 같이 호출 된다는 것을 더욱 엄격하게 검증하는 메소드.

it("should handle errors", async () => {
      const errorMessage = { message: "error" };
      const rejectedPromise = Promise.reject(errorMessage);
      productModel.findByIdAndDelete.mockReturnValue(rejectedPromise);
      await productController.deleteProduct(req, res, next);
      expect(next).toHaveBeenCalledWith(errorMessage);
   });

toBeTruthy() : 해당 expect가 true를 반환 한다는 것을 검증 할 때 사용.

it("should return 201 response code", () => {
      productController.createProduct(req, res, next);
      expect(res.statusCode).toBe(201);
      expect(res._isEndCalled()).toBeTruthy();
   });

beforeEach()

더보기

describe()it(), test() 메소드가 실행 될 때마다 정의 되어져야 하는 내용들을 입력하면 자동으로 반복을 하는 메소드.

let req, res, next;
beforeEach(() => {
   req = httpMocks.createRequest();
   res = httpMocks.createResponse();
   next = null;
});

describe("Product Controller Create", () => {
   beforeEach(() => {
      req.body = newProduct;
   });

   it("should call ProductModel.create", () => {
      productController.createProduct(req, res, next);
      expect(productModel.create).toBeCalledWith(newProduct);
   });
});

ErrorTest

더보기

JS의 경우 일반적인 동기형 방식으로만 이루어지지 않고 비동기형식으로도 많이 진행이 되어짐. 그러한 과정에서 일반적인 방식으로 검사하는 방식으로 테스트 코드를 작성을 하게 되면 에러가 발생.

 

DB에서 제공하는 기능들은 기본적으로 비동기 방식으로 async await의 기능을 사용하여 비동기에서 반환되는 값들을 처리 해야 됨.

 

그리고 반환되는 객체가 Promise 이므로 reject 객체를 생성하여 다음 모듈에게 전달하는 방식으로 작성해야됨.

it("should handle errors", async () => {
      const errorMessage = { message: "description property not found" };
      const rejectedPromise = Promise.reject(errorMessage);
      productModel.create.mockReturnValue(rejectedPromise);
      await productController.createProduct(req, res, next);
      expect(next).toBeCalledWith(errorMessage);
   });

통합 테스트

더보기

설명

통합테스트는 모듈을 통합하는 단계에서 수행하는 테스트이다. 단위테스트에서 먼저 확인하고 모든 모듈이 잘 작동이 되는 것을 확인되었다면 모든 모듈을 연동하여 테스트 해야함.

 

목적

모듈끼리 상호 작용이 잘 이루어지는지 확인

통합하는 과정에서 오류를 발견할 수 있음

 

모듈

supertest

 

사용법

하나의 url에 요청을 보내게 되면 넘겨 받게 되는 respoone를 하나씩 비교 하면서 작성을 하여야 함.

it("POST /api/products", async () => {
   const response = await request(app).post("/api/products").send(newProduct);

   expect(response.statusCode).toBe(201);
   expect(response.body.name).toBe(newProduct.name);
   expect(response.body.description).toBe(newProduct.description);
   expect(response.body.price).toBe(newProduct.price);
})

Error를 처리하고자 하는 경우에는 error handler가 직접적으로 json으로 보내주는 코드로 보내주어야 에러처리가 가능함.

app.use((err, req, res, next) => {
   res.status(500).json({ error: err.message });
});
it("should return 500 on POST /api/products", async () => {
   const response = await request(app).post("/api/products").send({ name: "phone" });

   expect(response.statusCode).toBe(500);
   expect(response.body).toStrictEqual({
      error: "Product validation failed: description: Path `description` is required.",
   });
});

'Backend > Node.js' 카테고리의 다른 글

Node.js child_process  (1) 2024.01.08
Node.js Event Architecture  (0) 2023.12.14
Node.JS) Module Caching 을 알아보자  (0) 2023.09.14
Node.js 작동방식 및 Event loop  (0) 2023.06.26
Node.js  (0) 2023.06.26