【问题标题】:Mocking a class property set in the constructor using Sinon使用 Sinon 模拟构造函数中设置的类属性
【发布时间】:2019-12-12 11:39:00
【问题描述】:

我正在尝试模拟在构造函数中设置为默认值的类属性

class Files {
  constructor(queueNumber = 0) {
    this.queueNumber = queueNumber;
    this.dir = 'JiraResults';
    if (!fs.existsSync(this.dir)) {
      fs.mkdirSync(this.dir);
    }
  }
  ...
}

构造函数根据dir 属性创建目录和文件,对于测试,我想要另一个目录,所以我不需要移动真实目录来运行测试。

我尝试了许多方法来替换该属性,但所有方法都因Sinon的不同错误而失败。

第一次尝试:

const tempDir = 'JiraResults-TEMP';
let stubDir;

describe('Files', () => {
  before(() => {
    stubDir = sinon.stub(Files.prototype.constructor, 'dir').value(tempDir);
  }
  ...
}

这样我得到了错误TypeError: Cannot stub non-existent own property dir

第二次尝试

const tempDir = 'JiraResults-TEMP';
let stubDir;

describe('Files', () => {
  before(() => {
    stubDir = sinon.stub(Files.prototype, 'dir').value(tempDir);
  }
  ...
}

这样我得到了错误TypeError: Cannot stub non-existent own property dir

第三次尝试

const tempDir = 'JiraResults-TEMP';
let stubDir;

describe('Files', () => {
  before(() => {
    stubDir = sinon.stub(Files.prototype, 'this').value({
      dir: sinon.stub().returnsThis(tempDir),
    });
  }
  ...
}

这样我得到了错误TypeError: Cannot stub non-existent own property this


我还尝试了其他方法,但从未达到更换属性的地步。

我查看了 Sinon 文档,但似乎没有一个示例适用于构造函数类。

谁能给我一个关于如何替换此属性的工作示例?

谢谢。

【问题讨论】:

    标签: javascript unit-testing mocha.js sinon es6-class


    【解决方案1】:

    您可以直接更改dir 属性的值,以便被测方法将使用存根的dir

    例如

    files.js:

    class Files {
      constructor(queueNumber = 0) {
        this.queueNumber = queueNumber;
        this.dir = "JiraResults";
      }
    
      mkdir() {
        console.log("make dir: ", this.dir);
      }
    }
    
    module.exports = Files;
    

    files.test.js:

    const Files = require("./files");
    const sinon = require("sinon");
    
    describe("Files", () => {
      it("should use stubbed dir", () => {
        sinon.spy(console, "log");
        const instance = new Files();
        instance.dir = "stubbed dir";
        instance.mkdir();
        sinon.assert.calledWith(console.log, "make dir: ", "stubbed dir");
      });
    });
    

    单元测试结果:

    Files
    make dir:  stubbed dir
        ✓ should use stubbed dir
    
    
      1 passing (7ms)
    
    ---------------|----------|----------|----------|----------|-------------------|
    File           |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    ---------------|----------|----------|----------|----------|-------------------|
    All files      |      100 |      100 |      100 |      100 |                   |
     files.js      |      100 |      100 |      100 |      100 |                   |
     files.test.js |      100 |      100 |      100 |      100 |                   |
    ---------------|----------|----------|----------|----------|-------------------|
    

    更新:

    我认为这是不可能的。我们来看看下面的例子:

    files.js:

    const fs = require("fs");
    
    class Files {
      constructor(queueNumber = 0) {
        this.queueNumber = queueNumber;
        console.log("before: ", this.dir);
        this.dir = "JiraResults";
        console.log("after: ", this.dir);
        if (!fs.existsSync(this.dir)) {
          fs.mkdirSync(this.dir);
        }
      }
    }
    
    Files.prototype.dir = "";
    
    module.exports = Files;
    

    files.test.js:

    const Files = require("./files");
    const sinon = require("sinon");
    const fs = require("fs");
    
    describe("Files", () => {
      it("should use stubbed dir to mkdir", () => {
        sinon.stub(fs, "existsSync").returns(false);
        sinon.stub(fs, "mkdirSync");
        sinon.stub(Files.prototype, "dir").value("stubbed dir");
        console.log("stub dir");
        new Files();
        sinon.assert.calledWith(fs.existsSync, "stubbed dir");
        sinon.assert.calledWith(fs.mkdirSync, "stubbed dir");
      });
    });
    

    单元测试结果:

      Files
    stub dir
    before:  stubbed dir
    after:  JiraResults
        1) should use stubbed dir to mkdir
    
    
      0 passing (13ms)
      1 failing
    
      1) Files
           should use stubbed dir to mkdir:
         AssertError: expected existsSync to be called with arguments 
    JiraResults stubbed dir 
          at Object.fail (node_modules/sinon/lib/sinon/assert.js:106:21)
          at failAssertion (node_modules/sinon/lib/sinon/assert.js:65:16)
          at Object.assert.(anonymous function) [as calledWith] (node_modules/sinon/lib/sinon/assert.js:91:13)
          at Context.it (src/stackoverflow/59303752/files.test.js:1:2345)
    

    在实例化Files 类之前,我们可以成功地为属性dir 创建存根。但是在我们实例化类Files 之后。它将“JiraResults”字符串分配给属性dir,这意味着它将把stubbed dir替换为JiraResults

    共有三个选项:

    1. 为属性dirFiles.prototype.dir = 'JiraResults'设置默认值(但根据您的要求。)
    2. dir 作为参数传递给Files 类的构造函数。
    3. fs 操作提取到类似于原始anwser 的方法中。

    然后,为单元测试制作存根会更容易。

    【讨论】:

    • 我想过这种方法,但我遇到的问题是构造函数创建了文件夹。因此,在构造函数运行后更改属性不是一种选择。我更新了问题以显示这一点。无论如何,谢谢。
    • 有道理...我会将构造函数更改为具有默认值,但允许替换我的测试。非常感谢
    猜你喜欢
    • 1970-01-01
    • 2013-01-12
    • 1970-01-01
    • 2017-11-01
    • 1970-01-01
    • 2020-07-11
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多