【问题标题】:Unit testing dependency injections单元测试依赖注入
【发布时间】:2021-06-19 10:10:20
【问题描述】:

我想在一个类中模拟依赖,但我找不到正确的方法来实现它。使用 sinon 进行单元测试。例如:

const { classB } = require("./classes/classB");
const { classC } = require("./classes/classC");
const sinon = require("sinon");

class classA {
    constructor() {
        this.classB = new classB();
        this.classC = new classC();
    }
 
    doSomething() {
        const data = this.classB.getInfo();
        const processed = processInfo(data);
        this.classC.processedData(processed);
        
    }
    
    processInfo(data) {
        // doesSomeProcessing
    }
}

module.exports = {
    classA
};

describe("classA", () => {
    describe("when instantiated", () => {
        it("calls classB's getInfo once, classC's processedData data once.", () => {
            sinon.stub(classB);
            let clsA = new classA();
            sinon.assert.calledOnce(classB.getInfo);
            sinon.assert.calledOnce(classC.processedData);
            // Also can we do something like this?
            // when classB.getinfo.then(provideMockData) 
            // so that we can mock the calls being made to avoid actual calls?
        });
    });
});

我尝试对此进行研究,但找不到可行的解决方案。任何见解都会非常有帮助!提前致谢,对造成的任何问题深表歉意!

【问题讨论】:

  • 不要在构造函数中实例化 b 和 c,将实例作为参数传递。然后在你的测试中通过间谍。
  • @JaredSmith 所以我们没有办法直接测试依赖关系吗?尽管这可行,但我不喜欢将实例作为参数传递的想法,原因之一是:当我的用例扩展时,我最终将这些参数传递给许多函数,从而导致代码异味。参考:refactoring.guru/smells/long-parameter-list
  • 是的,有一个很长的参数列表绝对是放弃依赖注入和编写难以测试的类的充分理由。尤其是在可以轻松处理部分应用程序和临时工厂功能的语言中。更不用说对对象参数进行解构以模拟关键字参数。您应该完全按照您的方式编写它

标签: javascript unit-testing mocking sinon sinon-chai


【解决方案1】:

您应该使用 Link Seams 来存根依赖关系。

例如

classA.js:

const { ClassB } = require('./classB');
const { ClassC } = require('./classC');

class ClassA {
  constructor() {
    this.classB = new ClassB();
    this.classC = new ClassC();
  }

  doSomething() {
    const data = this.classB.getInfo();
    this.classC.processedData(data);
  }
}

module.exports = { ClassA };

classB.js:

class ClassB {
  getInfo() {
    return 'real data';
  }
}

module.exports = { ClassB };

classC.js:

class ClassC {
  processedData(data) {
    return 'real process data';
  }
}

module.exports = { ClassC };

classA.test.js:

const sinon = require('sinon');
const proxyquire = require('proxyquire');

describe('66751571', () => {
  it('should pass', () => {
    const classBInstanceStub = {
      getInfo: sinon.stub().returns('teresa teng'),
    };
    const classCInstanceStub = {
      processedData: sinon.stub(),
    };
    const ClassBStub = sinon.stub().returns(classBInstanceStub);
    const ClassCStub = sinon.stub().returns(classCInstanceStub);
    const { ClassA } = proxyquire('./classA', {
      './classB': { ClassB: ClassBStub },
      './classC': { ClassC: ClassCStub },
    });
    const clsA = new ClassA();
    clsA.doSomething();
    sinon.assert.calledOnce(classBInstanceStub.getInfo);
    sinon.assert.calledWithExactly(classCInstanceStub.processedData, 'teresa teng');
  });
});

单元测试结果:

  66751571
    ✓ should pass (2326ms)


  1 passing (2s)

-----------|---------|----------|---------|---------|-------------------
File       | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-----------|---------|----------|---------|---------|-------------------
All files  |   81.82 |      100 |      50 |   81.82 |                   
 classA.js |     100 |      100 |     100 |     100 |                   
 classB.js |      50 |      100 |       0 |      50 | 3                 
 classC.js |      50 |      100 |       0 |      50 | 3                 
-----------|---------|----------|---------|---------|-------------------

【讨论】:

  • 正是我想要的!非常感谢@slideshowp2
猜你喜欢
  • 1970-01-01
  • 2017-04-01
  • 2021-05-29
  • 2018-03-03
  • 2017-11-03
  • 2016-02-16
  • 2010-10-23
相关资源
最近更新 更多