简体   繁体   English

如何使用 Sinon 存根 ES6 类构造函数

[英]How to stub ES6 class constructors with Sinon

I'm trying stub class constructors with Sinon.我正在尝试使用 Sinon 的存根类构造函数。

The stub of the regular method 'omg' works fine, but the stub of the constructor fails the test, and the 'real' constructor is called instead of the stub.常规方法“omg”的存根工作正常,但构造函数的存根未通过测试,并且调用“真实”构造函数而不是存根。

Any ideas on what syntax I need to stub this correctly?关于我需要正确存根的语法有什么想法吗?

 class Foo { constructor() { this.bar = new Bar(); } omg() { this.bar.omg(); } } class Bar { constructor() { console.log('In bar constructor'); } omg() { console.log('In bar omg'); } } const sandbox = sinon.createSandbox(); sandbox.stub(Bar.prototype, 'constructor'); sandbox.stub(Bar.prototype, 'omg'); describe('Foo', () => { describe('Constructor', () => { it('Should instantiate bar', () => { const foo = new Foo(); expect(Bar.prototype.constructor.called).to.be.true; }); }); describe('Omg', () => { it("Should call Bar's omg method', () => { const foo = new Foo(); foo.omg(); expect(Bar.prototype.omg.called).to.be.true; }); }); });

Its been more than 2 years, means this is hard.它已经超过2年了,这意味着这很难。 :) :)

When it is hard to do, then the code need refactoring.当它很难做到时,那么代码需要重构。

Below example works but may not usable at real world (need to define Bar as stub before define Foo).下面的示例有效,但在现实世界中可能无法使用(需要在定义 Foo 之前将 Bar 定义为存根)。

Highlight:亮点:

  • usage of stub calledWithNew() to check whether Bar called with new.使用stub calledWithNew()检查 Bar 是否使用 new 调用。
  • not use arrow functions .不使用箭头函数
  • Usage of chai's expect instanceof , because of Foo constructor will add property bar , then, additional expectation can be added to check property bar, which means check whether Foo's construction function run.使用 chai 的expect instanceof ,因为 Foo 的构造函数会添加属性bar ,然后可以添加额外的期望来检查属性 bar ,即检查 Foo 的构造函数是否运行。
// @file stackoverflow.js
const sinon = require('sinon');
const { expect } = require('chai');

const sandbox = sinon.createSandbox();

// Define Bar as a stub.
const Bar = sandbox.stub();
const BarOmgFn = sinon.fake();
Bar.prototype.omg = BarOmgFn;

// class Bar {
//   constructor() {
//     console.log('In bar constructor');
//   }
//   omg() {
//     console.log('In bar omg');
//   }
// }

class Foo {
  constructor() {
    this.bar = new Bar();
  }

  omg() {
    this.bar.omg();
  }
}

describe('Foo', function () {
  after(function () {
    sandbox.restore();
  });
  describe('Constructor', function () {
    it('Should instantiate bar', function () {
      const foo = new Foo();

      // Check whether Bar called with new.
      expect(Bar.calledWithNew()).to.equal(true);
      // Check bar property.
      expect(foo.bar).to.be.instanceOf(Bar);
    });
  });

  describe('Omg', () => {
    it("Should call Bar's omg method", function () {
      const foo = new Foo();
      foo.omg();

      expect(BarOmgFn.calledOnce).to.equal(true);
    });
  });
});

Run the example using mocha:使用 mocha 运行示例:

$ npx mocha stackoverflow.js


  Foo
    Constructor
      ✓ Should instantiate bar
    Omg
      ✓ Should call Bar's omg method


  2 passing (14ms)

$

From design pattern point of view, class Foo is highly dependent to class Bar.从设计模式的角度来看,Foo 类高度依赖于 Bar 类。 Refactoring can use dependency injection pattern .重构可以使用依赖注入模式 For example: simple change to class Foo, by require 1 argument, which is Bar itself or instance of Bar.例如:通过需要 1 个参数,即 Bar 本身或 Bar 的实例,对类 Foo 进行简单更改。 This change make the code easier to test.此更改使代码更易于测试。

class Foo {
  // Now constructor need 1 argument
  constructor(Bar) {
    this.bar = new Bar();
  }

  omg() {
    this.bar.omg();
  }
}

Hope this helps.希望这会有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM