1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

同じオブジェクトをテストによってスパイ、あるいはスタブで監視させる方法

Posted at

同じオブジェクトをテストによってスパイ/スタブを切り替えて監視したい場合の書き方。

NGパターン

スパイ、スタブは共に対象オブジェクトをラップするものである。スパイとスタブが同じテスト内で同じオブジェクトをラップするのは、スパイを二重にラップするのと等しいため、エラーとなる。

import * as sinon from 'sinon';

class SinonResetMixed {
  public methodToBeObserved(): void {
    // do nothing
  }
}

describe('Wraps same object by both spy and stub and uses simultaneously', () => {
  let instance: SinonResetMixed;
  let spy: sinon.SinonSpy;
  let stub: sinon.SinonStub;
  beforeAll(() => {
    spy = sinon.spy(SinonResetMixed.prototype, 'methodToBeObserved');
    stub = sinon.stub(SinonResetMixed.prototype, 'methodToBeObserved');
  });
  afterEach(() => {
    sinon.restore();
  });

  it('cannot wrap same object by both spy and stub', () => {
    instance = new SinonResetMixed();
    instance.methodToBeObserved();
    console.log(`Spy says methodToBeObserved has called ${spy.callCount} time(s)`);
    console.log(`Stub says methodToBeObserved has called ${stub.callCount} time(s)`);
    expect(spy.calledOnce).toBe(true);
    expect(stub.calledOnce).toBe(true);
  });
});
$ yarn test
 FAIL  src/SinonResetMixedNG.test.tsx
  ● Wraps same object by both spy and stub and uses simultaneously › cannot wrap same object by both spy and stub

    TypeError: Attempted to wrap methodToBeObserved which is already wrapped

      at checkWrappedMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:38:21)
      at wrapMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:85:13)
      at stub (node_modules/sinon/lib/sinon/stub.js:71:44)
      at Sandbox.stub (node_modules/sinon/lib/sinon/sandbox.js:291:33)
      at Object.<anonymous> (src/SinonResetMixedNG.test.tsx:15:18)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
      at process._tickCallback (internal/process/next_tick.js:68:7)
      --------------
      Error: Stack Trace for original
      at wrapMethod (node_modules/sinon/lib/sinon/util/core/wrap-method.js:110:31)
      at Function.spy (node_modules/sinon/lib/sinon/spy.js:45:16)
      at Sandbox.spy (node_modules/sinon/lib/sinon/sandbox.js:275:26)
      at Object.<anonymous> (src/SinonResetMixedNG.test.tsx:14:17)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
      at process._tickCallback (internal/process/next_tick.js:68:7)

  ● Wraps same object by both spy and stub and uses simultaneously › cannot wrap same object by both spy and stub

    TypeError: Cannot read property 'callCount' of undefined

      at Object.<anonymous> (src/SinonResetMixedNG.test.tsx:25:65)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
      at process._tickCallback (internal/process/next_tick.js:68:7)

  Wraps same object by both spy and stub and uses simultaneously
    ✕ cannot wrap same object by both spy and stub (1ms)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.944s, estimated 1s
Ran all test suites matching /ng/.

  console.log src/SinonResetMixedNG.test.tsx:26
    Spy says methodToBeObserved has called 1 time(s)

OKパターン

対象となるオブジェクトが同じでも、スパイとスタブが同じテスト内に共存しなければOK。

import * as sinon from 'sinon';

class SinonResetMixed {
  public methodToBeObserved(): void {
    // do nothing
  }
}

describe('Wraps same object by both spy and stub but uses separately', () => {
  let instance: SinonResetMixed;
  let spy: sinon.SinonSpy;
  let stub: sinon.SinonStub;
  afterEach(() => {
    sinon.restore();
  });

  it('only uses spy', () => {
    spy = sinon.spy(SinonResetMixed.prototype, 'methodToBeObserved');
    instance = new SinonResetMixed();
    instance.methodToBeObserved();
    console.log(`Spy says methodToBeObserved has called ${spy.callCount} time(s)`);
    expect(spy.calledOnce).toBe(true);
  });

  it('only uses stub', () => {
    stub = sinon.stub(SinonResetMixed.prototype, 'methodToBeObserved');
    instance = new SinonResetMixed();
    instance.methodToBeObserved();
    console.log(`Stub says methodToBeObserved has called ${stub.callCount} time(s)`);
    expect(stub.calledOnce).toBe(true);
  });
});
 PASS  src/SinonResetMixedOK.test.tsx
  Wraps same object by both spy and stub but uses separately
    ✓ only uses spy (2ms)
    ✓ only uses stub (5ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        1.031s
Ran all test suites matching /ok/.

  console.log src/SinonResetMixedOK.test.tsx:23
    Spy says methodToBeObserved has called 1 time(s)

  console.log src/SinonResetMixedOK.test.tsx:30
    Stub says methodToBeObserved has called 1 time(s)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?