経験は何よりも饒舌

10年後に真価を発揮するかもしれないブログ 

Jestのカバレッジ計測について

Jestのカバレッジ計測について、次のコードで調べてみる。

function sum(a, b) {
  return a + b;
}

function minus(a, b) {
  return a - b
}

module.exports = {
  sum: sum,
  minus: minus
}

sumだけテストしてみる。

const { sum } = require('./sample');

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
});

結果は、Funcsが50%、Linesが66.66%になっている。
Funcssumminusのうちsumしかテストしていないので1/2で50%、行数は66%程度なんだな、と納得できる。

$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 PASS  ./sample.test.js
  sample
    ✓ sum (2 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   66.66 |      100 |      50 |   66.66 |                   
 sample.js |   66.66 |      100 |      50 |   66.66 | 6                 
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.506 s, estimated 1 s

次のようなテストを書けば、予想通りカバレッジは100%になる。

const { sum, minus } = require('./sample');

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
  it('minus', () => {
    expect(minus(2, 1)).toBe(1);
  })
});
$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 PASS  ./sample.test.js
  sample
    ✓ sum (1 ms)
    ✓ minus

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 sample.js |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.642 s, estimated 1 s

次に、やむを得ない状況が発生して一方でNode.jsのassert module、一方でJestのExpectを使用している場合を想定する。

const assert = require("assert")
const { sum, minus } = require('./sample');

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
  assert.equal(minus(2, 1), 1);
});

カバレッジを出してみると、予想に反して100%になる。
てっきりJestのExpectを使用していないと計測できない(50%になる)と思っていた。

$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 PASS  ./sample.test.js
  sample
    ✓ sum (1 ms)
    ✓ minus

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 sample.js |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        0.642 s, estimated 1 s

つまり、次のようにテストをせずただ意味もなく呼び出しただけでもカバレッジは100%になる

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
  minus()
});
$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 PASS  ./sample.test.js
  sample
    ✓ sum (2 ms)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 sample.js |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        0.527 s, estimated 1 s

次に、minusのテストが通らない場合を想定する。
双方にJestのExpectを使った場合はカバレッジは100%になる。

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
  it('minus', () => {
    expect(minus(2, 1)).toBe(5);
  })
});
$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 FAIL  ./sample.test.js
  sample
    ✓ sum (1 ms)
    ✕ minus (2 ms)

  ● sample › minus

    expect(received).toBe(expected) // Object.is equality

    Expected: 5
    Received: 1

       7 |   })
       8 |   it('minus', () => {
    >  9 |     expect(minus(2, 1)).toBe(5);
         |                         ^
      10 |   })
      11 | });
      12 |

      at Object.<anonymous> (sample.test.js:9:25)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |     100 |      100 |     100 |     100 |                   
 sample.js |     100 |      100 |     100 |     100 |                   
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total
Time:        0.572 s, estimated 1 s

次のようにminusでNode.jsでassertが通らない場合は、sumのテストが通るにも関わらず、カバレッジは0%になる。

describe('sample', () => {
  it('sum', () => {
    expect(sum(1, 2)).toBe(3);
  })
  assert.equal(minus(2, 1), 5);
});
$ npm run test

> jest-playground@1.0.0 test
> jest --collectCoverage

 FAIL  ./sample.test.js
  ● Test suite failed to run

    AssertionError [ERR_ASSERTION]: 1 == 5

       6 |     expect(sum(1, 2)).toBe(3);
       7 |   })
    >  8 |   assert.equal(minus(2, 1), 5);
         |          ^
       9 | });
      10 |

      at sample.test.js:8:10
      at Object.<anonymous> (sample.test.js:4:1)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |       0 |        0 |       0 |       0 |                   
----------|---------|----------|---------|---------|-------------------
Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        0.599 s, estimated 1 s
まとめ
  • JestはJestのAPIを用いたテストに対してカバレッジを計測しているのではなく、テストで呼び出されたか否かでカバレッジを計測している。
  • Node.jsのassert moduleを使ってテストが通らなかった場合は、カバレッジの計測は機能しない。
  • Stmts, Branchは追って調査していく