I'm trying to mock a class property that is set to a default value inside the constructor
class Files {
constructor(queueNumber = 0) {
this.queueNumber = queueNumber;
this.dir = 'JiraResults';
if (!fs.existsSync(this.dir)) {
fs.mkdirSync(this.dir);
}
}
...
}
The constructor creates directories and files based on the dir
property and for the tests, I want another directory, so I don't need to move the real directory to run the tests.
I tried many approaches to replace the property and all of them kept failing with different errors from Sinon.
const tempDir = 'JiraResults-TEMP';
let stubDir;
describe('Files', () => {
before(() => {
stubDir = sinon.stub(Files.prototype.constructor, 'dir').value(tempDir);
}
...
}
With this I get the error 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);
}
...
}
With this I get the error 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),
});
}
...
}
With this I get the error TypeError: Cannot stub non-existent own property this
I also tried other things and never got to the point of having the property replaced.
I looked into Sinon documentation, but none of the examples seems to apply to a constructor class.
Could anyone give me a working example on how can I replace this property?
Thanks.
You can change the value of the dir
property directly so that the method under test will use the stubbed dir
.
E.g.
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");
});
});
Unit test result:
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 | |
---------------|----------|----------|----------|----------|-------------------|
UPDATE:
I don't think it's possible. Let's take a look at below example:
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");
});
});
Unit test result:
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)
We can make a stub for property dir
successfully before instantiating the class Files
. But after we instantiated the class Files
. It will assign "JiraResults" string to property dir
which means it will replace the stubbed dir
to JiraResults
.
There are three options:
dir
, Files.prototype.dir = 'JiraResults'
(But it is based on your requirement.)dir
as a parameter to the constructor of Files
class.fs
operations to a method like the original anwser.Then, it will be easier to make a stub for unit testing.