使用 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
.
共有三个选项:
- 为属性设置一个默认值
dir
,Files.prototype.dir = 'JiraResults'
(但根据您的要求。)
- 将
dir
作为参数传递给Files
class的构造函数。
- 将
fs
操作提取到类似于原始答案的方法。
然后,为单元测试制作存根会更容易。
我正在尝试模拟在构造函数中设置为默认值的 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
.
共有三个选项:
- 为属性设置一个默认值
dir
,Files.prototype.dir = 'JiraResults'
(但根据您的要求。) - 将
dir
作为参数传递给Files
class的构造函数。 - 将
fs
操作提取到类似于原始答案的方法。
然后,为单元测试制作存根会更容易。