쿡북 for 단위 테스트 stub/spy/mock편

소개



이 문서는 단위 테스트를 쓸 때 "왜 spy라면 반환 값을 mock 할 수 없어!?"라든지 "어떻게 이 라이브러리의 메소드 어떻게 mock하면 좋을까?"라든지 "spy와 mock과 stub의 구분이 전혀 모르겠다・・・」라고 하는 고민을 몇번이나 반복한 끝에 그 자리에서 벗어난 테스트 코드를 계속 쓴 결과, 니치도 얼마든지 가지 않아 테스트가 쓰기 어려워져 버린 코드를 메인트하는 트러미에 부딪혀 있었다 자신을 위해서, 「다음은 여기를 보고 제대로 테스트 쓴다・・・!」라고 하는 결의를 하기 위한 문서입니다.

요점은 캄페

아, 덧붙여서 일단 조사하면서 쓰고 있습니다만, 깊이는 원하지 않기 때문에 착각이 있을지도・・・ 눈치채는 곳이 있으면 편집 리퀘스트 주면 기쁩니다.

대상 환경



모의 도서관에 jest/sinon
테스트 러너는 jest
NodeJs에서 개발한 애플리케이션에 대한 단위 테스트를 가정합니다.

항상 작성해 갈 것을 상정하고 있기 때문에, 버전은 그 때의 stable 최신을 상정하고 있습니다.
2019/05/17 현재는 jest 24.8 Sinon.Js는 7.3.2에서 움직이고 있습니다.

sinon



스파이



spy한 메소드가 몇번 실행되어, 반환값은 무엇으로 무슨 인수가 건네졌는지 등, 다양한 정보를 기억한다.
spy한 메소드는 기본적으로 그대로 실행된다
또한 자신이 조사한 한 spy 한 객체의 메서드의 반환 값을 모의하는 방법을 찾을 수 없었다.

stub



Q.stub은 어떤 때에 사용합니까?

A. 테스트에서 메서드의 동작을 제어하고 싶을 때 사용합니다.
예를 들어 오류 처리 또는 테스트를 원할 때
XMLHttpRequest 라든지 readFile 라든지 테스트시에 실행해 원하지 않는 메소드를 실행시키지 않기 위해서 사용하는 경우도 있군요.

즉 테스트 내부의 프로그램의 거동을 컨트롤하고 싶을 때 사용하는 것이 좋을 것 같다.sinon.assert.calledWith(stbObj, arg1, arg2,..) 라든가를 이용하면 건네받은 인수의 테스트라도 할 수 있다.

mock



다시 이번

jest



다시 이번

사용 사례



fs 시스템 라이브러리의 메소드를 조롱하고 싶습니다.


readFileSync 라든가 그런 녀석. 글쎄, 고마워.

그런 쉬운 일! readFileSync를 조롱하는 것만으로!
하지만 만약을 위해 (?) console.log에서 모의 ​​내용을 보라!

index.js

const fs = require('fs');
const main = () => {
    const res = fs.readFileSync('index.js');
    console.log(res)
    return res;
}

module.exports.main = main;

index.spec.js

const sinon = require('sinon'),
  fs = require('fs'),
  assert = require('assert'),
  index = require('../index');
describe('test index.js', () => {
  let stubFs;
  beforeEach(() => {
    stubFs = sinon.stub(fs, 'readFileSync').callsFake(() => '')
  });
  it('execute console.log', () => {
    assert.equal(index.main(), '');
  })
});

테스트 결과
 FAIL  test/index.spec.js
  test index.js
    ✕ execute console.log (8ms)

  ● test index.js › execute console.log

    TypeError: Cannot read property 'charCodeAt' of undefined

      at _callsites (node_modules/@jest/source-map/build/getCallsite.js:19:39)
      at _default (node_modules/@jest/source-map/build/getCallsite.js:85:21)
      at Function.write (node_modules/@jest/console/build/BufferedConsole.js:104:51)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        2.055s
Ran all test suites matching /test\/index.spec.js/i.



위의 스텁의 방법이라고 하면, 모든 readFileSync 의 반환값을 mocks 로 해 버리고 있다.
상당히 많기 때문에 console.log 뿐만 아니라 config 라이브러리 를 사용하고 있는 경우라든지 readFileSync 를 stub 하면 영향이 나오는 것 같다.

stub.withArgs(arg1[, arg2, ...]);


Stubs the method only for the provided arguments.
주어진 인수 만 메소드를 스텁한다고 쓰고 있습니까?

그러면 이제 인수를 지정하면 특정 인수를 주어진 함수만 스텁하거나 그렇게 훌륭할 수 있다. -> 할 수 없었다.
지정한 인수 이외라면 값이 돌아오지 않는다. (문서에도 그렇게 써있다)

stub.callThrough();


Causes the original method wrapped into the stub to be called when none of the conditional stubs are matched.

매우 그렇다. 이것을 사용하여 테스트를 수정했습니다.

index.spec.js
const sinon = require('sinon'),
  fs = require('fs'),
  assert = require('assert'),
  index = require('../index');
describe('test index.js', () => {
  let stubFs;
  beforeEach(() => {
    stubFs = sinon.stub(fs, 'readFileSync').withArgs('index.js').callsFake(() => '')
   fs.readFileSync.callThrough();
  });
  it('execute console.log', () => {
    assert.equal(index.main(), '');
  })
});

이것으로 index.js 이외의 인수가 readFileSync 에 건네졌을 때는 통상의 메소드가 불리게 되었다. 했어.

클래스의 메소드를 스텁하고 싶습니다.


sinon.stub(Class.prototype, 'method')

갈 수 있습니다.

좋은 웹페이지 즐겨찾기