TIL 50 | Unit Test

dabin *.◟(ˊᗨˋ)◞.*·2021년 10월 20일
1

etc

목록 보기
7/14

세상에 완벽한 코드는 없다. 하지만 코드를 보완할 방법은 있다. 여러가지의 테세트를 통해 모든(?) 경우의 수에 대비할 수 있다. Google Test Automation Conference에서 제안된 테스트 피라미드를 살펴보자.

출처 : https://cloud.google.com/architecture/devops/devops-tech-test-automation

구글에서 제안한 테스트 비율은 E2E(UI) testing 10%, Integration test 20%, unit test 70%다.

  • E2E(End-To-End Testing): 프론트엔드와 백엔드가 만나는 테스트로 그 중에서도 끝이 만난다. 쉽게 말하면 UI를 테스트하는 것이다. 시간이 오래 걸리고 정확도가 조금 떨어진다는 점이 특징이다. 따라서 테스트 중 비율이 적다. 큰 회사의 경우 이 테스트만 하는 직군도 있다.(QA)
  • Integration Test: 최소 두개 이상을 통합한 테스트 단위로 테스트하는 것. 묶음이라고 생각하자. 백엔드 테스트에서는 최소 두개 이상의 API를 사용하는 것을 의미한다. 개별적으로 관리하기가 굉장히 까다롭다는 단점이 있다. 따라서 비율이 적다.
  • Unit Test: 여기서의 unit은 함수의 단위다. 유닛테스트를 잘 짜놓으면 유지 보수가 쉽다는 장점이 있다.(feat. 노가다...)

이제, Jest(코드를 테스트하는 라이브러리 중 하나)를 사용해 Unit Test를 작성해보자.

  • 설치
npm install --sav-dev jest

dev dependancy 하는 이유는 개발할 때만 사용하기 때문이다. 배포시에 사용하지 않는다.

  • Jest에서 es6문법 사용
npm i --save-dev babel-jest @babel/core @bable/present-env

// .babelrc.json 파일 생성 후 추가
{
	"presets": [["@babel/preset-env", {"targets": {"node": "current"}}]],
}

// package.json

{
	"scripts": {
    "test": "jest",
  },
}

여기까지 하면 테스트 실행할 환경이 만들어진다. 테스트를 실행할 때 모든 파일을 테스트할 필요가 없으니, 어떤 파일이 테스트 코드가 들어있는 파일인지 알려줘야 한다. 세가지 규칙 중 하나를 지켜 제스트가 알 수 있게 해준다.

  • __test__ 폴더에 .js 확장자 파일
  • .test.js 확장자 파일
  • .spec.js 확장자 파일

이제 테스트 코드를 작성해보자.

  • it, test : 테스트의 가장 작은 단위, it과 test의 차이는 없다!
  • describe : 테스트 케이스 묶음
  • describe(큰 단위) 안에 it, test(작은 단위)가 들어간다.
  • 첫 번째 인자에 테스트 이름, 두 번째 인자에 콜백함수를 넣는다. 테스트 코드는 비동기적으로 실행된다.
  • 테스트 케이스 안에서 expect와 비교하는 다양한 메서드를 사용할 수 있다.
    참고링크 : https://jestjs.io/docs/using-matchers
  • 유닛 테스트를 작성할 때 최소 3가지 경우에 대비하자. 성공케이스는 1, input이 없을 때 0, 이상한 input이 들어왔을 경우 -1
//calculate.js
function minus(a, b) {
  if (typeof a === "number" && typeof b === "number") {
    return a - b;
  } else {
    throw new Error("wrong input");
  }
}

export default {minus}

//calculate.test.js
import {minus} from './calculate';

describe("minusTest", () => {
  test("minus 9 - 5 to equal 3", () => {
    expect(minus(9, 5)).toEqual(4);
  });

  test("no input", () => {
    expect(() => minus()).toThrowError(new Error("wrong input"));
  });

  test("one input", () => {
    expect(() => minus(1)).toThrowError(new Error("wrong input"));
  });
});

에러를 new Error()로 던지게 되면 함수를 실행하는 순간 에러가 나기 때문에, 콜백함수로 넘겨야 에러가 넘어간다는 점을 기억하자.

테스트 코드를 작성했으면 run test를 한다.

npm run test

터미널에 PASS가 뜨면 성공이다!

우리 팀은 아래 에러 함수에 대한 테스트 코드를 작성했다.

const errorStatus = (statusCode, message) => {
  if (!Object.keys(httpErrorStatus).includes(statusCode)) {
    statusCode = 500;
  }
  const err = new Error(message || httpErrorStatus[statusCode]);
  err.statusCode = statusCode;
  throw err;
};

export default errorStatus;

테스트 코드가 반복되어 for...of를 사용했다.

describe('errorStatus', () => {
  const httpErrorStatus = {
    400: 'Bad Request',
    ...
  };

  //1: status코드가 있는 경우
  for (const key in httpErrorStatus) {
    test(`status가 ${key}인 경우`, () => {
      expect(() => {
        errorStatus(key, null);
      }).toThrowError(new Error(httpErrorStatus[key]));
    });
  }

  //0: status와 message가 없는 경우
  test('status 없는 경우', () => {
    expect(() => {
      errorStatus();
    }).toThrowError(new Error(httpErrorStatus[500]));
  });

  //-1: httpErrorStatus에 없는 statusCode가 들어왔을 경우
  test('httpErrorStatus에 없는 statusCode', () => {
    expect(() => {
      errorStatus(800, null);
    }).toThrowError(new Error(httpErrorStatus[500]));
  });
});

두번째 인자로 콜백함수를 써줘야 하는데, 콜백함수를 작성하지 않아 우리 팀의 두시간이 쓱싹 지나가버렸다. 확인 또 확인하는 습관을 갖자..ㅎㅎ

profile
모르는것투성이

2개의 댓글

comment-user-thumbnail
2021년 10월 21일

이거 거의 세션 자료수준..

1개의 답글