코드 커버리지

코드 커버리지 (Code Coverage)

테스트가 코드를 얼마나 잘 실행했는지 수치로 보고 싶다면 코드 커버리지를 켜면 돼요. Jest는 테스트 실행 중 어떤 파일의 어느 라인이 실제로 실행됐는지를 측정해, 커버리지 리포트를 만들어 줘요. 아직 테스트 파일이 없는 파일까지 포함하여 측정 대상을 지정할 수 있고, 커버리지 하한선을 설정해 기준 미달이면 테스트를 실패시키는 **게이트(gate)**로도 쓸 수 있죠.

Jest 공식 문서에서 커버리지는 Configuration 레퍼런스의 설정 항목으로 다뤄져요. 여기서는 커버리지와 직접 관련된 옵션들을 모아 설명할게요.

출처: Jest 공식 문서 — Configuration (커버리지 설정)

커버리지 수집 켜기: collectCoverage

기본값은 false예요. 테스트를 실행하면서 커버리지 정보를 수집할지 여부를 나타내요. 이 옵션은 실행된 모든 파일에 커버리지 수집 구문을 덧대기 때문에 테스트가 눈에 띄게 느려질 수 있어요.

Jest는 두 가지 커버리지 프로바이더를 제공해요. 기본값인 babelv8이죠. 자세한 내용은 coverageProvider 항목을 보세요.

const {defineConfig} = require('jest');

module.exports = defineConfig({
  collectCoverage: true,
});

:::info

babel 프로바이더는 /* istanbul ignore next */ 주석으로, v8 프로바이더는 /* c8 ignore next */ 주석으로 리포트에서 라인을 제외해요. 더 자세한 내용은 istanbuljs 문서c8 문서를 참고하세요.

:::

커버리지 대상 지정: collectCoverageFrom

기본값은 undefined예요. 커버리지 정보를 수집할 파일 집합을 가리키는 glob 패턴의 배열이에요. 파일이 지정한 glob에 매치되면, 그 파일에 테스트가 없거나 테스트 스위트에서 한 번도 require되지 않아도 커버리지 정보를 수집해요.

const {defineConfig} = require('jest');

module.exports = defineConfig({
  collectCoverageFrom: [
    '**/*.{js,jsx}',
    '!**/node_modules/**',
    '!**/vendor/**',
  ],
});
import {defineConfig} from 'jest';

export default defineConfig({
  collectCoverageFrom: [
    '**/*.{js,jsx}',
    '!**/node_modules/**',
    '!**/vendor/**',
  ],
});

이 설정은 프로젝트 rootDir 안의 모든 파일에서, **/node_modules/** 또는 **/vendor/**에 매치되는 파일을 제외한 나머지의 커버리지를 수집해요.

:::tip

각 glob 패턴은 설정에 적힌 순서대로 적용돼요. 예를 들어 ["!**/__tests__/**", "**/*.js"]는 두 번째 패턴이 부정(negation)을 덮어쓰므로 __tests__를 제외하지 못해요. 부정 glob이 동작하게 하려면 **/*.js 뒤에 와야 하죠.

:::

:::note

이 옵션은 collectCoveragetrue이거나 Jest를 --coverage와 함께 실행해야 동작해요.

:::

jest --coverage로 실행했는데 아래처럼 coverage 데이터를 찾지 못한다는 메시지가 나온다면, 대개 glob 패턴이 어떤 파일도 매치하지 못한 거예요.

=============================== Coverage summary ===============================
Statements   : Unknown% ( 0/0 )
Branches     : Unknown% ( 0/0 )
Functions    : Unknown% ( 0/0 )
Lines        : Unknown% ( 0/0 )
================================================================================
Jest: Coverage data for global was not found.

이때는 micromatch 문서를 참고해 glob이 호환되는지 확인하세요.

커버리지 출력 위치: coverageDirectory

기본값은 undefined예요. Jest가 커버리지 파일을 출력할 디렉토리를 지정해요.

커버리지 제외: coveragePathIgnorePatterns

기본값은 ["/node_modules/"]예요. 모든 파일 경로에 대해 테스트 실행 전에 대조되는 정규식 패턴 문자열의 배열이에요. 파일 경로가 어떤 패턴에 매치되면 커버리지 정보를 건너뛰어요.

이 패턴은 전체 경로에 대조돼요. 프로젝트 루트 경로를 포함하려면 <rootDir> 토큰을 써서, 서로 다른 환경에서 다른 루트 디렉토리를 가져 모든 파일이 실수로 무시되는 걸 막을 수 있어요. 예: ["<rootDir>/build/", "<rootDir>/node_modules/"].

커버리지 프로바이더: coverageProvider

커버리지를 위해 코드를 계측(instrument)할 프로바이더를 지정해요. 허용 값은 기본값인 babel 또는 v8이에요.

커버리지 리포트 형식: coverageReporters

기본값은 ["clover", "json", "lcov", "text"]예요. Jest가 커버리지 리포트를 쓸 때 사용하는 리포터 이름 목록이에요. istanbul 리포터는 모두 사용할 수 있어요.

:::tip

이 옵션을 설정하면 기본값을 덮어써요. 콘솔 출력에서 커버리지 요약을 보려면 "text""text-summary"를 추가하세요.

:::

튜플 형태로 추가 옵션을 전달할 수도 있어요. 예를 들어 완전히 커버된 파일의 리포트 라인을 숨길 수 있어요.

const {defineConfig} = require('jest');

module.exports = defineConfig({
  coverageReporters: ['clover', 'json', 'lcov', ['text', {skipFull: true}]],
});
import {defineConfig} from 'jest';

export default defineConfig({
  coverageReporters: ['clover', 'json', 'lcov', ['text', {skipFull: true}]],
});

옵션 객체 형태에 대한 자세한 내용은 타입 정의CoverageReporterWithOptions 타입을 참고하세요.

커버리지 하한선: coverageThreshold

기본값은 undefined예요. 커버리지 결과의 최소 임계값을 강제하는 데 사용돼요. 임계값은 global로, glob으로, 그리고 디렉토리나 파일 경로로 지정할 수 있어요. 임계값을 충족하지 못하면 jest는 실패해요.

  • 임계값이 양수면, 필요한 커버리지의 최소 퍼센트로 해석돼요.
  • 임계값이 음수면, 허용되는 커버되지 않은 항목의 최대 개수로 처리돼요.

예를 들어 아래 설정으로는 branch·line·function 커버리지가 80% 미만이거나, 커버되지 않은 statement가 10개보다 많으면 jest가 실패해요.

const {defineConfig} = require('jest');

module.exports = defineConfig({
  coverageThreshold: {
    global: {
      // Requires 80% branch coverage
      branches: 80,
      // Requires 80% function coverage
      functions: 80,
      // Requires 80% line coverage
      lines: 80,
      // Require that no more than 10 statements are uncovered
      statements: -10,
    },
  },
});
import {defineConfig} from 'jest';

export default defineConfig({
  coverageThreshold: {
    global: {
      // Requires 80% branch coverage
      branches: 80,
      // Requires 80% function coverage
      functions: 80,
      // Requires 80% line coverage
      lines: 80,
      // Require that no more than 10 statements are uncovered
      statements: -10,
    },
  },
});
  • coverageThreshold.global.lines [number]: 라인에 대한 전역 임계값
  • coverageThreshold.global.functions [number]: 함수에 대한 전역 임계값
  • coverageThreshold.global.statements [number]: statement에 대한 전역 임계값
  • coverageThreshold.global.branches [number]: branch에 대한 전역 임계값

glob 패턴에 매치되는 파일에 대해서는 coverageThreshold[glob-pattern]으로 별도 임계값을 설정할 수 있어요. 이렇게 하면 높은 전역 기준을 유지하면서도 중요 파일이나 디렉토리에 특정 임계값을 둘 수 있죠.

커버리지 CLI 명령

설정 파일 없이 커버리지를 켜고 싶다면 CLI에서 --coverage 플래그를 사용하면 돼요.

jest --coverage

더 알아보기

커버리지와 함께 쓰는 목 함수로 테스트를 바꿔보고 싶다면 Mock Functions을, 커버리지 시리얼라이저를 포함한 전체 설정 옵션 목록은 Configuration 문서를 참고하세요.