I'm just getting into unit testing for the first time. Using Mocha in Node as the testing framework. All the examples I've come across create variables inside the it(). Does it matter if they are created inside or outside of it()? For example, if I have multiple it()s inside a describe(), and I need the same mocked data across all of the it()s. I'd rather not re-create the same variable repeatedly, if possible.
describe ('MyClass', function () {
let myObj = new MyObj // Mock data here
it ('Should be...', function () {
....
})
it ('Should be...', function () {
....
})
...
})
It's totally acceptable to have variables live outside of your individual it blocks, but it may not be appropriate depending on your use case.
For objects that you do not expect to change, Object.freeze is an option: const myObj = Object.freeze(new MyObj).
If you expect your tests to change your object, you should use beforeEach to ensure that they are restored to the proper state; this will prevent your it blocks from polluting one another and avoid an unpleasant debugging journey.
For example:
describe('MyClass', function () {
let myObj
beforEach(() => {
myObj = new MyObj()
})
it('changes myObj', () => {
changeProp(myObj.sum)
expect(myObj.sum).toEqual(4)
})
it('depends on myObj being the same', () => {
expect(myObj.sum).toEqual(2)
})
})
Alternately, you can eschew the fat arrow syntax and rely on the shared context between blocks in mocha:
beforeEach(function () {
this.myObj = new MyObj()
})
it('changes myObj', function () {
addTwo(this.myObj.sum)
expect(this.myObj.sum).toEqual(4)
})
it('depends on myObj being the same', function () {
expect(this.myObj.sum).toEqual(2)
})
Related
How can we do callback on success and failue cases for below lines of code for test coverage using jest
const handleService = () => {
window.domain.service("1321",'',onSuccess, onFailure)
}
const onSuccess = () => {
....update state values
}
const onFailure = () => {
....update state values
}
Something like this:
Spy on window.domain.service to gain access to the calls it receives. This will allow you to access the parameters of those calls which will be "1321",'',onSuccess, onFailure
Assign the function you wish to test to a variable
Invoke the function to execute the code in it (this will get you the coverage)
(Optional) assert that the callback functions behave correctly
Here is a snippet to help demonstrate
it('should run', () => {
// Some setup to create the function on the window, may not be needed if done elsewhere.
// Could be good to do this in a beforeEach and clean up in afterEach to avoid contaminating the window object
window.domain = {
service: () => {},
}
// Spy on the window.domain.service method.
// Provide a mock implementation if you don't want the real one to be called
const serviceSpy = jest.spyOn(window.domain, 'service');
executeYourCode();
// capture the arguments to the call
const [_arg1, _arg2, onSuccess, onFailure] = serviceSpy.mock.calls[0];
// execute the callbacks
onSuccess();
onFailure();
});
The scenario
I am writing unit tests for a module similar to the one below
service.js
const client = require("./../client");
let managedData = {};
function manageData(dataId) {
let users = client.retrieveUsers(); //Will return an array of strings. For example: a list of user names
let user = client.retrieveCurrentUser(); //Will return a string. For example: one user name
let userIsPresentInList = users.includes(user); //Check if the user is present in a broader user list
if(userIsPresentInList) {
managedData[dataId] = client.retrieveDataWithId(dataId);
}
}
function unmanageData(dataId) {
client.doSomethingToTheDataWithId(dataId)
delete managedData[dataId]
}
module.exports = {
manageData,
unmanageData
}
Some of my unit tests target what happens when the user is present in the list of users, i.e., what happens inside the if-clause
The questions is: if I am unit testing an if-clause, should I mock everything up to the clause (option 1 below)? Or should I create an way of "directly" ending up in the if-clause (option 2 below)?
The test
Option 1) Group the tests via describe blocks and mock in beforeEach/beforeAll:: Scenarios that concern what's inside the if-clause are grouped in a describe block and mocks are done for all statements up until the if-clause
Option 2) Mock intermediate functions inside module: service.js is refactored. The code that checks whether the user is in the list of users is extracted in a new function inside the module. This function then is mocked. In this way anything before the if-clause does not really matter and the test only concerns what is inside the if-clause is working
service.test.js
const service = require("./service");
const client = require("./../client");
jest.mock("./../client");
beforeEach(() => {
jest.clearAllMocks();
});
describe('Sucesful management', () => {
//OPTION 1
beforeEach(() => {
client.retrieveUsers = jest.fn(() => ["a", "b", "c"]);
client.retrieveCurrentUser = jest.fn(() => "a");
})
//OPTION 2
beforeEach(() => {
service.isUserPresentInUsersList = jest.fn(() => true);
})
afterEach(() => {
service.isUserPresentInUsersList.mockRestore();
})
it("Given the user belongs to the user list, should retrieve the data calleing manageData", async () => {
//Act
service.manageData("D");
//Assert
expect(client.retrieveDataWithId).toHaveBeenCalledWith("D");
});
it("Given the user belongs to the user list, should do something with the data when calling unmanageData", async () => {
//Act
service.manageData("D");
service.unmanageData("D");
//Assert
expect(client.doSomethingToTheDataWithId).toHaveBeenCalledWith("D");
});
})
Is there preferable one in this case? Is any of those approaches terrible?
An extended question would be: what are open source projects that have good testing practices that I can get inspiration from?
I try to call a lot of async functions in my mocha JS before hook but they are executed at last. Basically I am trying to execute repeated tests with different params from the constructor initialization.
I tried with only one function but it also execute at last.Tried to pass done() function to inner async function but it doesnt help either.
a.test.js and base.tests.js files :
describe('Test block', () => {
before((done) => {
const baseClass = new baseClass()
baseTests.applyTests(done)
});
describe('test',()=>{
....first which should be executed;
})
}
----------------------------------------------------------------
class baseClass {
constructor() {
init smth....
}
async applyTests(done) {
await Promise.All(
[
a(),
b(),
c()
]
done();
)
}
async a() {
return describe('TEST', () => {
it('TEST', (done) => {
chai.request(server)
.get(url)
.end((err, res) => {
asserts...
done();
});
});
}}
I expect to run first the async operations in the before hook and after that all other tests.
Keep in mind that describe and it blocks do not execute any tests; they add tests to the test list that Mocha will execute. Putting a describe block inside a function called from a before hook like this won't ever end up executing code like chai.request(... etc. (And even if it did, it would be totally broken: a before hook is run before each test, you don't want to do asserts there because it won't be linked to any particular unit test.)
I can't give more concrete advice because I'm not sure what you were trying to accomplish, but in general, your describe and it blocks should be top-level constructs and not put inside other code like this.
EDIT: Just to make the execution path here clear: your before hook runs, which calls applyTests, which calls a(), which executes a describe block and adds your unit test TEST to the test list, then it returns. It then begins running tests, including test and TEST.
EDIT: Ah, makes sense, let me suggest two patterns that are often used in unit tests.
Pattern 1: "Test Loop"
This pattern creates many similar-looking tests, using an array of input params to produce appropriate test descriptions and test bodies.
[
{ foo: "milk", bar: "smoothie" },
{ foo: "yogurt", bar: "fridge" },
{ foo: "whatever", bar: "container" }
].forEach(test => {
it("puts the " + test.foo + " in the " + test.bar, function () {
assert(subject.someMethod(foo) === bar);
});
});
Pattern 2: "Test Helper"
This pattern creates individual tests, but puts a common test body in a helper method so it can be reused.
function testSomeMethod(foo, bar) {
assert(subject(foo) == "bar");
}
it("puts the milk in the fridge", function () {
testSomeMethod("milk", "fridge");
});
it("puts the cereal in the pantry", function () {
testSomeMethod("cereal", "pantry");
});
Examples above are very simple, but either the test loop or the test helper pattern can be used to encapsulate a much more complicated series of steps (set up a request, look at some response headers / bodies, etc.).
I can't figure out a way to stub a function called from within the same module this function is defined (the stub does not seem to work). Here's an example:
myModule.js:
'use strict'
function foo () {
return 'foo'
}
exports.foo = foo
function bar () {
return foo()
}
exports.bar = bar
myModule.test.js:
'use strict'
const chai = require('chai')
const sinon = require('sinon')
chai.should()
const myModule = require('./myModule')
describe('myModule', () => {
describe('bar', () => {
it('should return foo', () => {
myModule.bar().should.equal('foo') // succeeds
})
describe('when stubbed', () => {
before(() => {
sinon.stub(myModule, 'foo').returns('foo2') // this stub seems ignored
})
it('should return foo2', () => {
myModule.bar().should.equal('foo2') // fails
})
})
})
})
This reminds me of Java static functions which are not stubbable (almost).
Any idea how to achieve what I'm trying to do? I know that extracting foo in a different module will work, but that's not what I'm trying to do here. I'm also aware that invoking foo in the bar method with the keyword this will also work, I'm puzzled toward the use of ̀this in this context (since I'm not using OOP).
I just tested this. And it works like charm.
'use strict'
function foo () {
return 'foo';
}
exports.foo = foo;
function bar () {
return exports.foo(); // <--- notice
}
exports.bar = bar;
Explanation
when you do sinon.stub(myModule, 'foo').returns('foo2') then sinon stubs the exported object's foo not the actually foo function from inside your myModule.js ... as you must know, foo is in accessible from outside the module. So when you set exports.foo, the exported object exports.foo stores the ref of foo. and when you call sinon.stub(myModule, 'foo').returns('foo2'), sinon will stub exports.foo and not the actual foo
Hope this makes sense!
I was a bit wary of using exports since it's a bit magical (for instance when you're coding in Typescript, you never use it directly), so I'd like to propose an alternate solution, which still requires modifying the source code unfortunately, and which is simply to wrap the function to be stubbed into an object:
export const fooWrapper = {
foo() {...}
}
function bar () {
return fooWrapper.foo()
}
And sinon.stub(fooWrapper, 'foo'). It's a bit a shame having to wrap like that only for testing, but at least it's explicit and type safe in Typescript (contrary to to exports which is typed any).
I'm trying to create mocha tests for my controllers using a config that has to be loaded async. Below is my code. However, when the mocha test is run, it doesn't run any tests, displaying 0 passing. The console.logs are never even called. I tried doing before(next => config.build().then(next)) inside of the describe, but even though the tests run, before is never called. Is there a way to have the config be loaded one time before any tests are run?
'use strict';
const common = require('./common');
const config = require('../config');
config
.build()
.then(test);
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
require('./auth');
});
}
You should run Mocha with the --delay option, and then use run() once you are done building your test suite. Here is an example derived from the code you show in the question:
'use strict';
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
it("test", () => {
console.log(3);
});
});
// You must use --delay for `run()` to be available to you.
run();
}
setTimeout(test, 1000);
I'm using setTimeout to simulate an asynchronous operation. Using --delay and run() allows you to build a suite that is the result of an asynchronous computation. Note, however, that the suite must be built in one shot. (You cannot have an asynchronous process inside describe that will make calls to it. This won't work.)
One thing you should definitely not do is what rob3c suggests: calling describe or it (or both) from inside a hook. This is a mistake that every now and then people make so it is worth addressing in details. The problem is that it is just not supported by Mocha, and therefore there are no established semantics associated with calling describe or it from inside a hook. Oh, it is possible to write simple examples that work as one might expect but:
When the suite becomes more complex, the suite's behavior no longer corresponds to anything sensible.
Since there are no semantics associated with this approach, newer Mocha releases may handle the erroneous usage differently and break your suite.
Consider this simple example:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]);
describe("top", () => {
let flag;
before(() => {
flag = true;
return p.then((names) => {
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
});
});
after(() => {
flag = false;
});
it("regular test", () => {
assert(flag);
});
});
When we run it, we get:
top
✓ regular test
embedded
1) foo
2) bar
3) baz
1 passing (32ms)
3 failing
// [stack traces omitted for brevity]
What's going on here? Shouldn't all the tests pass? We set flag to true in the before hook for the top describe. All tests we create in it should see flag as true, no? The clue is in the output above: when we create tests inside a hook, Mocha will put the tests somewhere but it may not be in a location that reflects the structure of the describe blocks in the code. What happens in this case is that Mocha just appends the tests created in the hook the the very end of the suite, outside the top describe, so the after hook runs before the dynamically created tests, and we get a counter-intuitive result.
Using --delay and run(), we can write a suite that behaves in a way concordant with intuition:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]).then((names) => {
describe("top", () => {
let flag;
before(() => {
flag = true;
});
after(() => {
flag = false;
});
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
it("regular test", () => {
assert(flag);
});
});
run();
});
Output:
top
✓ regular test
embedded
✓ foo
✓ bar
✓ baz
4 passing (19ms)
In modern environments, you can use top-level await to fetch your data up front. This is a documented approach for mocha: https://mochajs.org/#dynamically-generating-tests
Slightly adapting the example from the mocha docs to show the general idea:
function fetchData() {
return new Promise((resolve) => setTimeout(resolve, 5000, [1, 2, 3]));
}
// top-level await: Node >= v14.8.0 with ESM test file
const data = await fetchData();
describe("dynamic tests", function () {
data.forEach((value) => {
it(`can use async data: ${value}`, function () {
// do something with data here
});
});
});
This is nice as it is on a per-file basis, and doesn't involve you taking on management responsibility of the test runner as you do with --delay.
The problem with using the --delay command line flag and run() callback that #Louis mentioned in his accepted answer, is that run() is a single global hook that delays the root test suite. Therefore, you have to build them all at once (as he mentioned), which can make organizing tests a hassle (to say the least).
However, I prefer to avoid magic flags whenever possible, and I certainly don't want to have to manage my entire test suite in a single global run() callback. Fortunately, there's a way to dynamically create the tests on a per-file basis, and it doesn't require any special flags, either :-)
To dynamically create It() tests in any test source file using data obtained asynchronously, you can (ab)use the before() hook with a placeholder It() test to ensure mocha waits until before() is run. Here's the example from my answer to a related question, for convenience:
before(function () {
console.log('Let the abuse begin...');
return promiseFn().
then(function (testSuite) {
describe('here are some dynamic It() tests', function () {
testSuite.specs.forEach(function (spec) {
it(spec.description, function () {
var actualResult = runMyTest(spec);
assert.equal(actualResult, spec.expectedResult);
});
});
});
});
});
it('This is a required placeholder to allow before() to work', function () {
console.log('Mocha should not require this hack IMHO');
});