본문 바로가기
테스트코드 & 정적분석

단언문 (expect/assert) 안에서 비교하지 않기

by 향로 (기억보단 기록을) 2022. 4. 10.
반응형

간혹 코드를 보면 expect 에서 비교를 하는 코드를 보곤 한다.
이를테면 다음과 같은 경우이다.

it('getCount의 결과는 5보다 크다', () => {
    const result = getCount();

    expect(result > 5).toBe(true);
});

당장 봐서는 문제가 없어보인다.
오히려 비교값이 더 큰지 검증하는 jest 함수를 별도로 찾아보지 않아도 된다는 장점도 있어보인다.

비슷한 예제로 다음과 같은 코드도 종종 보게 된다.

it('getCount의 값은 10과 동일하다', () => {
    const count = getCount();

    expect(count === 10).toBe(true);
});

하지만 이 테스트 코드들은 크게 2개의 단점을 갖고 있다.
(정확히는 잘못된 assertion 을 사용하고 있다.)

첫번째는 실패할 경우 실패 원인을 찾기가 어렵다.

이를테면 아래의 코드로 실패할 경우

it('getCount의 값은 10과 동일하다', () => {
    const count = getCount();

    expect(count === 10).toBe(true);
});

결과는 다음과 같이 출력된다.

fail1

검증 대상이 count === 10 의 결과인 true 혹은 false 이기 때문에, 실패시 출력되는 실제 받은 값(Received) 역시 true/false 만 나오게 된다.

테스트가 실패했지만 getCount의 결과는 어디서도 확인할 수 없게 된다.

반면에 다음과 같이 테스트 코드를 작성한다면

it('getCount의 값은 10이다', () => {
    const count = getCount();

    expect(count).toBe(10);
});

검증 대상이 getCount() 이기 때문에 실패시 실제 받은 값(Received)을 쉽게 확인할 수 있다.

fail2

테스트 코드는 언제든지 실패할 수 있기 때문에 실패시 얼마나 빨리 원인을 찾을 수 있냐는 굉장히 중요하다.
이게 갖춰지지 않으면 테스트 코드 수정하는데 대부분의 시간을 보내게 된다.

또 하나의 단점은 사람의 언어로 코드가 작성되지 않는다는 점이다.

이건 사람마다 크게 체감하는 바가 다른것 같다.

이를테면 첫번째 코드와 두번째 코드를 사람의 언어로 설명하면 다음과 같다.

  • expect(result === 10).toBe(true)
    • getCount의 결과가 10인 것은 참이다.
  • expect(getCount()).toBe(10)
    • getCount의 결과는 10이다.

둘의 코드를 사람의 언어로 치환해보면 첫번째 코드가 얼마나 어색한지 체감된다.

테스트 코드는 구현 코드 보다 훨씬 더 사람의 언어를 사용해야한다고 믿고 있다.
테스트 코드는 당장 PO나 기획자분들이 사용하는 언어 그대로 구현되어야만 코드를 해석하는 노력이 거의 없다고 생각한다.
그리고 그런 테스트 코드가 결국 오랫동안 유지보수 되고, 프로젝트를 지킬 수 있다.

그래서 단언문(expect/assert) 안에서는 절대 검증하지 않는 것이 좋다.

여담이지만 비슷한 예로 Java로 한다면 다음과 같다.

//bad
assertTrue(a == 1)

//good
assertThat(a).is(1)
반응형