Istanbul로 코드 커버리지 측정하기

노드, 익스프레스로 모바일 API 서버를 구현하는 것이 위플래닛에서의 나의 주업무다. 요구사항에 있는 기능 대부분을 구현하고 나면 조금씩 리펙토링과 테스트 코드를 작성하면서 시간을 보낸다. 테스트를 할 때 작성한 모든 코드에 대한 테스트를 하고 싶은 것이 개발자의 욕심이다. 그래서 어떤 부분을 놓쳤는지를 찾아보는 것이 필요한데 이것을 코드 커버리지(coverage) 분석이라고 한다.

아래 링크는 코드 커버리지에 대한 설명이다.

코드 커버리지는 테스트 코드에 의해 실제 코드가 얼마나 동작했는지를 측정하는 지표다.

예전에 Hapi로 프로젝트를 진행하면서 테스트 코드를 작성한 적이 있는데 이때는 Lab이라는 툴로 코드 커버리지를 측정했다. (Hapi 테스트 코드 참고) 분석 결과를 보면서 테스트가 부족한 부분의 코드에 대한 테스트 코드를 작성했던 기억이 난다.

익스프레스에는 모카(mocha)로 유닛테스트를 진행하는데 커버리지 분석하는 툴이 당연히 있겠지 싶어 찾아봤는데 이스탄불(istanbul)이라는 툴을 발견했다. 이스탄불을 통해 어떻게 유닛테스트 환경을 개선할 수 있을지 정리해 봤다.

이스탄불로 코드 커버리지 측정하기

우선 코드와 유닛테스트 코드를 작성해 보자.

math.js:

function add(a, b) {
  return a + b
}

module.exports = {
  add: add,
}

math.spec.js:

const assert = require("assert")
const math = require("./math")

describe("math.js", () => {
  describe("add()", () => {
    it("1 add 2 should return 3", () => {
      assert(math.add(1, 2) === 3)
    })
  })
})

모카와 이스탄불 모듈을 설치한다.

$ npm install mocha --save-dev
$ npm install istanbul --save-dev

아래 명령어를 실행하면 mocha로 유닛테스트를 실행하고 이스탄불로 코드 커버리지 분석결과를 출력해 준다.

$ node_modules/.bin/istanbul cover node_modules/.bin/_mocha math.spec.js

> istanbul-test@1.0.0 coverage /Users/jeonghwan/Development/istanbul-test
> istanbul cover node_modules/.bin/_mocha math.spec.js

  math.js
    add()1 add 2 should return 3

  1 passing (5ms)

=============================================================================
Writing coverage object [/Users/jeonghwan/Development/istanbul-test/coverage/coverage.json]
Writing coverage reports at [/Users/jeonghwan/Development/istanbul-test/coverage]
=============================================================================

=============================== Coverage summary ===============================
Statements   : 100% ( 9/9 )
Branches     : 100% ( 0/0 )
Functions    : 100% ( 1/1 )
Lines        : 100% ( 9/9 )
================================================================================

마지막 부분에 Coverage summary 부분을 보면 각 분석 항목별로 측정 수치를 보여주고 있는데 그 의미는 다음과 같다.

  • Statements: 전체 코드중 명령문이 몇 개이고 얼마나 실행되었는가?
  • Branches: 전체 코드중 분기문이 몇 개이고 얼마나 실행되었는가?
  • Functions: 전체 코드중 함수가 몇 개이고 얼마나 실행되었느가?
  • Lines: 전체 코드라인이 몇 개이고 얼마나 많이 실행되었는가?

Branches Coverage

네 가지 분석결과 중에 Branches에 대해 어떻게 사용하는지 알아보자. 코드에 분기문을 추가하고 테스트를 돌려보자.

function add(a, b) {
  if (a === null) {
    a = 0
  }

  return a + b
}
=============================== Coverage summary ===============================
Statements   : 93.33% ( 14/15 )
Branches     : 50% ( 1/2 )
Functions    : 100% ( 0/0 )
Lines        : 93.33% ( 14/15 )
================================================================================

Branches 커버리지 분석결과 2개의 분기문 중 하나만 실행되어 50%의 측정 수치가 나왔다. 이스탄불은 좀 더 상세한 분석 결과를 웹페이지로 만들어준다. 아래 파일을 브라우져로 열어보면 다음과 같다.

open coverage/lcov-report/index.html

분기문이 실행되지 않은 부분이 빨간색으로 표시된다. 이 코드가 테스트 중에 실행되지 않았고 우리는 이 부분을 위한 테스트 코드를 추가하면서 테스트를 개선할 수 있다.

math.spec.js:

it("0 add 2 should return 2", () => {
  assert(math.add(null, 2) === 2)
})

Functions Coverage

이번에는 Functions Coverage가 어떻게 이뤄지는지 알아보자. math.js에 sub() 함수를 만들고 테스트를 수행하면 아래 결과가 나온다.

=============================== Coverage summary ===============================
Statements   : 93.33% ( 14/15 )
Branches     : 100% ( 4/4 )
Functions    : 50% ( 1/2 )
Lines        : 93.33% ( 14/15 )
================================================================================

총 2개의 함수중 하나의 함수만 수행되었기 때문에 Fucntions: 50%의 결과가 나왔다.

이것도 테스트 코드를 추가함으로서 개선할 수 있다.

math.spec.js:

describe("sub()", () => {
  it("1 sub 1 should return 0", () => {
    assert(math.sub(1, 1) === 0)
  })
})

다시 결과를 확인해보면 모두 100%다.

=============================== Coverage summary ===============================
Statements   : 100% ( 18/18 )
Branches     : 100% ( 4/4 )
Functions    : 100% ( 2/2 )
Lines        : 100% ( 18/18 )
================================================================================

Statements, Lines Coverage

이 커버리지는 비슷한 성격으로 전체 코드라인(명령문) 중 얼마나 실행되었는지 측정하는 분석이다. 원리는 위에 설명한 것들과 비슷하다.

코드 커버리지를 분석하면 누락한 테스트코드를 만드는데 도움을 준다. 하지만 100% 완벽한 커버리지를 만들었다고 해도 버그는 여전히 잠재해 있음을 참고하자.