使用 Sinon 在构造函数中模拟 class 属性 集

Mocking a class property set in the constructor using Sinon

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

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 文档,但 none 的示例似乎适用于构造函数 class。

任何人都可以给我一个关于如何替换这个 属性 的工作示例吗?

谢谢。

您可以直接更改 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/Whosebug/59303752/files.test.js:1:2345)

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

共有三个选项:

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

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