320x100
320x100

단위 테스트 (Unit Test)

: 컴퓨터 프로그래밍에서 소스 코드의 특정 모듈이 의도된 대로 정확히 검증하는 절차

: 모든 함수와 메소드에 대한 테스트 케이스를 작성하는 절차

: 유닛 테스트 메서드를 따로 작성하여 전체 프로그램을 실행하지 않고 특정 루틴을 테스트

 

- 유닛테스트를 하는 이유

: 버그를 줄이고 코드 퀄리티를 높이기 위함

: 의존성이 있는 다른 클래스들에서 버그가 나는 것을 방지하지 위함

 

- Stub

: 테스트 중인 모듈이 호출하는 다른 소프트웨어 구성요소를 일시적으로 대체하는 소프트웨어 구성요소

: 유닛 A를 테스트할 때 유닛 A가 호출하는 유닛 B 대신 작성하는 가짜 객체 Stub

ex) 메일 보내기 모듈을 테스트 할 때 구현되지 않은 수신 확인 모듈을 대체하여 Stub 작성

 

- Stub를 사용하는 이유

: 구현되지 않은 함수나 라이브러리에서 제공하는 함수를 사용할 때

: 함수가 반환하는 값을 임의로 생성하고 싶을때 (예외처리를 위함)

: 복잡한 논리 흐름을 가지는 경우 테스트를 단순화 하고 싶을때

: 의존성을 가지는 유닛의 응답을 모사하여 독립적인 시험 수행을 하고자 할 때

 

 

 

 

 

통합 테스트 (Integration Test)

: 모듈을 통합하는 과정에서 모듈간의 호환성을 확인하기 위해 수행되는 테스트

: 독립적인 기능의 테스트가 아닌 웹 페이지로 부터 API를 호출하여 올바르게 동작하는지 확인

 

 

 

 

 

단위 테스트를 수행하는 방법

- 직접 함수 작성

: 작성한 모듈을 호출하여 결과를 받아내는 함수 (stub)를 작성

: 만든 Stub을 실행하여 결과 확인

 

- 유닛테스트 라이브러리 사용

: jest

: mocha

: chai

: sinon (mocking)

 

 

 

jest를 통한 node JS 모듈 테스트를 위한 stub 작성

- 테스트를 수행할 모듈 작성 (sum.js)

function sum(a, b) {
  return a + b;
}
module.exports = sum;

 

- stub 작성 (sum.test.js)

test('two plus two is four', () => {
  expect(sum(2, 2)).toBe(4);
});

※ test(name, fn, timeout)

: name = 테스트에 대한 설명

: fn = 함수

: timeout = 응답제한 시간

 

※ expect(value)

: .toBe와 같은 mather와 함께 사용

: value로는 함수를 넣어서 테스트 

 

※ .toBe(value)

: 기대하는 값

 

 

 

- describe 작성

const myBeverage = {
  delicious: true,
  sour: false,
};

describe('my beverage', () => {
  test('is delicious', () => {
    expect(myBeverage.delicious).toBeTruthy();
  });

  test('is not sour', () => {
    expect(myBeverage.sour).toBeFalsy();
  });
});

※ describe(name, fn)

: 서로 관련있는 테스트를 묶음

: name = 테스트 그룹의 이름

: fn = 테스트 함수들의 묶음

 

 

 

 

- mocking

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

※ mocking

: 임시의 테스트용 함수를 만드는 것

 

 

- mocking 테스트용 모듈 (forEach.test.js)

forEach = require('./forEach');

test('forEach()', () => {
  const mockCallback = jest.fn(x => 42 + x);
  forEach([0, 1], mockCallback);

  // The mock function is called twice
  // same as expect(mockCallback).toBeCalledTimes(2);
  expect(mockCallback.mock.calls.length).toBe(2);

  // The first argument of the first call to the function was 0
  expect(mockCallback.mock.calls[0][0]).toBe(0);

  // The first argument of the second call to the function was 1
  expect(mockCallback.mock.calls[1][0]).toBe(1);

  // The return value of the first call to the function was 42
  expect(mockCallback.mock.results[0].value).toBe(42);
})

※ jest.fn(implemetation)

: 모킹할 내용

: 위 예제에서는 임의로 작성한 forEach 함수에 x => 42 + x 라는 값을 넣어 호출한 것

 

※ mockFn.mock.calls

: i번째 호출에서의 j번째 인자를 갖는 배열

 

※ mockFn.mock.results

: type과 value라는 객체를 요소로 하는 배열

 

※ mockFn.mockName(value)

: value = mock 함수의 이름

: jest.fn() 대신 이름을 붙일때 사용

 

 

 

const mock = jest.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43

※ mockFn.mockReturnValue(value)

: mock 함수의 리턴값을 지정

 

 

 

- 모듈 mocking (banana.js)

// banana.js
module.exports = () => 'banana';

// __tests__/test.js
jest.mock('../banana');

const banana = require('../banana'); // banana will be explicitly mocked.

banana(); // will return 'undefined' because the function is auto-mocked.

: jest.mock에 모킹할 모듈의 이름을 넘김

 

 

 

- 모킹한 모듈 호출

jest.mock('../models/user');
const User = require('../models/user');
const {addFollowing} = require('./user');

describe('addFollowing', ()=>{
  const req = {
    user : {
      id : 1,
    },
    params : {
      id : 2,
    }
  };
  const res = {
    status : jest.fn(()=>res),
    send : jest.fn(),
  };
  const next = jest.fn();

  test('사용자를 찾아 팔로잉을 추가하고 success를 응답해야 함', async()=>{
    User.findOne.mockReturnValue({
      addFollowing(id){
        return Promise.resolve(true);
      }
    });
    await addFollowing(req, res, next);
    expect(res.send).toBeCalledWith('success');
  });

  test('사용자를 못 찾으면 res.status(404).send(no user)를 호출함', async()=>{
    User.findOne.mockReturnValue(null);
    await addFollowing(req, res, next);
    expect(res.status).toBeCalledWith(404);
    expect(res.send).toBeCalledWith('no user');
  });

  test('DB에서 에러가 발생하면 next(error)를 호출함', async()=>{ 
    const error = '테스트용 에러';
    User.findOne.mockReturnValue(Promise.reject(error));
    await addFollowing(req, res, next);
    expect(next).toBeCalledWith(error);
  });
})

 

 

 

 

 

 

 

jest를 통한 node JS 모듈 테스트

- 프로젝트에 jest 설치

: npm install -D jest

 

{
  ...
  "scripts": {
    "start": "nodemon app",
    "test": "jest"
  },
  ...
}

- test 코드 실행을 위한 스크립트 추가

: package.json의 scripts에 test 추가

 

- 테스트 실행

: npm test

: 자동으로 파일명에 test나 spec이 들어간 파일들을 모두 찾아서 실행

 

 

 

 

 

 

 

 

 

 

 

 

Refference

 

단위시험을 여행하는 테스터를 위한 테스트 스텁의 정의와 사용법

컴퓨터 프로그래머나 테스터들이라면 한번쯤 스텁(Stub)이라는 얘기를 들어본 적 있을 것이다. 위키에서는...

blog.naver.com

 

그래서 유닛테스트(Unit Test)가 뭔가요?

소프트웨어 업계에 종사하거나 개발을 많이 해봤다면 유닛테스트에 대해 종종 들었을 것이다. 업계에 종사하고있다면 실제로 유닛테스트를 매일 작성하고 있을수도 있다. 유닛테스트는 뭘까?

imasoftwareengineer.tistory.com

 

[TDD] 단위 테스트(Unit Test) 작성의 필요성 (1/3)

1. 단위 테스트 vs 통합 테스트 차이 [ 단위 테스트(Unit Test) ] 단위 테스트(Unit Test)는 하나의 모듈을 기준으로 독립적으로 진행되는 가장 작은 단위의 테스트이다. 여기서 모듈은 애플리케이션에

mangkyu.tistory.com

 

nodejs 테스트 도구와 방법론 (테스트의 중요성, 전략, mocha, chai, sinon, istanbul, 유용한 팁)

1. 테스트를 왜 해야하는가? 테스트를 안하는 개발자는 없다. 코드 작성 후 서버를 뛰워서 api url을 호출해서 응답값을 확인해보고, UI에서 버튼을 눌러보고 하는 것도 모두 테스트이다. 그러나 여

sjh836.tistory.com

 

[Node.JS] 개발 후 테스트 하기 - 유닛 테스트

개발을 마친 후 배포하기 전에는 반드시 테스트를 해야합니다. 노드로 만든 서버를 테스트를 할 때는 jest라는 패키지를 사용합니다.jest는 페이스북에서 만든 테스트 패키지입니다.​test를 입력

velog.io

300x250
728x90