I have this function:
let removePresentation = function(presentationName, callback) {
let rimraf = require('rimraf');
callback();
callback();
callback();
if(!presentationName || !presentationName.trim()) {
callback();
return;
}
presentationName = presentationName.replace('.zip', '');
rimraf('./presentations/' + presentationName, function(err) {
if(err) {
console.log(err);
}
callback();
});
};
exports.removePresentation = removePresentation;
and I am trying to test it with the following:
var chai = require('chai'),
expect = require('chai').expect,
sinonChai = require('sinon-chai'),
sinon = require('sinon'),
mock = require('mock-require');
chai.use(sinonChai);
describe('removePresentation', function() {
var sandbox;
var callback;
var rimrafSpy;
beforeEach(function() {
sandbox = sinon.sandbox.create();
mock('../business/communications_business', {});
rimrafSpy = sinon.spy();
callback = sinon.spy();
mock('rimraf', rimrafSpy);
});
afterEach(function() {
sandbox.restore();
});
it('should call rimraf if presentation name is valid', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation('name.zip', callback);
expect(rimrafSpy).to.have.been.calledWith('./presentations/name');
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is null', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation(null, callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is whitespace', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation(' ', callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
it('should not call rimraf if presentation name is empty string', function(done) {
let RoomStateBusiness = require('../business/roomstate_business');
RoomStateBusiness.removePresentation('', callback);
expect(rimrafSpy).not.to.have.been.called;
expect(callback).to.have.been.called.once;
done();
});
});
Even though I am clearly calling callback() multiple times (whilst testing only), expect(callback).to.have.been.called.once; is always asserting to true. I have checked on the Chai api that that expects the call to be exactly once, although it is always passing no matter how many times I call callback(). What am I doing wrong?
There is no such assertion as expect(fn).to.have.been.called.once.
As per sinon-chai docs, there is only:
expect(fn).to.have.been.called
expect(fn).to.have.been.calledOnce
The problem
This is a known problem with chai and why getter-only-assertions are a bad thing. Chai allows you to write a piece of code which looks like a property access (ie. the assertion itself does not end with a function call) to assert...whatever you want to assert. This uses property getters to execute the necessary code.
The problem is that if you make a typo or other mistake, the expression will simply evaluate to undefined (you are accessing a property which does not exist) and no assertion code is ever executed, resulting in a test passing (because tests only fail if an exception is thrown).
In your case, there is an assertion for called, and this most likely returns an object. Unfortunately, that object does not have assertion once, so there is no code executed and the test passes.
The solution
There are 2 options available to you:
Upgrade to Chai 4 and Node.js version with Proxy support (not sure where Proxy support was added, probably Node.js 5 or 6) - chai introduced a safeguard against these issues by proxying all property access through a Proxy object which checks if you are using a valid assertion
Never use getters for assertions and always end your assertions with a function call - this will make sure that if you ever make a mistake the test will fail with the infamous undefined is not a function error
The second option is highly preferred, in my opinion, as there can be no doubt on the correctness of the test case. Chai proxy support can still be turned off even on supported platforms.
Assuming we are talking about sinon-chai it should be calledOnce not called.once. If you do called any number of calls > 0 will pass the test.
Related
I call this function, console log is called but done() is not called:
import {Database} from "../../code/server/Database";
import 'mocha'
const expect = require('chai').expect
describe('Database save', function() {
it('should save without error', function(done) {
Database.saveSessionData(1, 2, 3, function(err, docs) {
expect(err).to.equal(null)
expect(docs.sessionOpenTime).to.equal(1)
expect(docs.sessionCloseTime).to.equal(2)
expect(docs.sessionLength).to.equal(3)
console.log(2222)
done()
})
})
})
Here is the result, 'Running tests' continues spinning on forever:
But if I change the 'Database' code into this, it works as expected:
setTimeout(function () {
console.log('lol')
done()
}, 1000)
What am I missing here?
Mocha test hangs since you have an opened database connection.
There are two options to solve this problem:
If you do not need a real database connection in your tests:
you can use sinon.stub() (https://www.npmjs.com/package/sinon) to return a predetermined response for async methods you use in your tests or sinon.spy() to make sure a stubbed method called exact number of times.
Here's a good article I just found to illustrate how to use sinon.js: https://semaphoreci.com/community/tutorials/best-practices-for-spies-stubs-and-mocks-in-sinon-js.
you can implement a dependency injection container to be able to replace your implementation of Database class to a Database class that does not perform I/O operations.
Although dependency injection implementations may vary depending on requirements some simple implementations are also available:
https://blog.risingstack.com/dependency-injection-in-node-js/
If you need to perform a real connection in your tests:
Consider adding an after hook to your mocha tests:
let's say mongodb is used as a database (it does not matter, but it would
be an actual working example)
const mongoose = require('mongoose');
const expect = require('chai').expect;
mongoose.connect('mongodb://localhost/test');
after(() => {
mongoose.connection.close();
});
describe('db connection', () => {
it('should make a test', () => {
expect(1).to.equal(1);
});
});
I am writing a node application with mocha and chai. Some of the tests call an external API for integration tests, which might take up to 90sec to perform the action.
In order to cleanup properly, I defined an afterEach()-block, which will delete any generated remote resources, in case an expect fails and some resources weren't deleted.
The tests themselves have an increased timeout, while the rest of the tests should retain their default and small timeout:
it('should create remote resource', () => {...}).timeout(120000)
However, I can't do the same with afterEach().timeout(120000), because the function does not exist - nor can I use the function ()-notation due to the unknown resource names:
describe('Resources', function () {
beforeEach(() => {
this._resources = null
})
it('should create resources', async () => {
this._resources = await createResources()
expect(false).to.equal(true) // fail test, so...
await deleteResources() // will never be called
})
afterEach(async() => {
this._resources.map(entry => {
await // ... delete any created resources ...
})
}).timeout(120000)
})
Any hints? Mocha is version 4.0.1, chai is 4.1.2
The rules are same for all Mocha blocks.
timeout can be set for arrow functions in Mocha 1.x with:
afterEach((done) => {
// ...
done();
}).timeout(120000);
And for 2.x and higher it, beforeEach, etc. blocks are expected to be regular functions in order to reach spec context. If suite context (describe this) should be reached, it can be assigned to another variable:
describe('...', function () {
const suite = this;
before(function () {
// common suite timeout that doesn't really need to be placed inside before block
suite.timeout(60000);
});
...
afterEach(function (done) {
this.timeout(120000);
// ...
done();
});
});
Mocha contexts are expected to be used like that, since spec context is useful, and there are virtually no good reasons to access suite context inside specs.
And done parameter or promise return are necessary for asynchronous blocks.
If you need to use dynamic context you have to use normal function.
describe('Resources', function () {
// ...
afterEach(function (){
this.timeout(120000) // this should work
// ... delete any created resources ...
})
})
I am reading some tutorials on promise tests in mocha. There is a piece of codes:
before(function(done) {
return Promise.resolve(save(article)).then(function() {
done();
});
});
Why done() called in the then() in the before()? What is the difference between the above codes and the following codes:
before(function(done) {
return Promise.resolve(save(article));
});
Thanks
UPDATE
My question is to compare with the following codes:
before(function() {
return Promise.resolve(save(article));
});
Sorry for the typo.
The first code snippet with the before hook returns a promise and calls done. In Mocha 3.x and over, it will result in this error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
It used to be that it did not particularly matter if you used done and returned a promise, but eventually the Mocha devs figured that specifying both done and returning a promise just meant the test designer made a mistake and it was better to have Mocha pitch a fit rather than silently allow it.
In your 2nd snippet, you have the done argument and return a promise but Mocha will still wait for done to be called and will timeout. (It really should detect the argument and raise an error like in the 1st case, but it doesn't...)
Generally, if you are testing an asynchronous operation that produces a promise, it is simpler to return the promise than use done. Here's an example illustrating the problem:
const assert = require("assert");
// This will result in a test timeout rather than give a nice error
// message.
it("something not tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
});
});
// Same test as before, but fixed to give you a good error message
// about expecting a value of 2. But look at the code you have to
// write to get Mocha to give you a nice error message.
it("something tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
}).catch(done);
});
// If you just return the promise, you can avoid having to pepper your
// code with catch closes and calls to done.
it("something tested properly but much simpler", () => {
return Promise.resolve(1).then((x) => {
assert.equal(x, 2);
});
});
With regards to the completion of asynchronous operations, it works the same whether you are using it, before, beforeEach, after or afterEach so even though the example I gave is with it, the same applies to all the hooks.
I am not sure if I understood 100% the question, but the tests will not start until done is called.
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
This test will not start until the done function is called in the call to beforeEach above. And this spec will not complete until its done is called.
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
You don't have to pass done in your example, just:
before(function() {
return Promise.resolve(save(article));
});
If you do pass done the test runner will expect to be called before continue, otherwise it will probably throw a timeout error.
In this particular case there is no functional difference. The callback, often called done, was introduced to handle asynchronous tests when using callbacks. Returning a Promise is sufficient, but note that you cannot define the done callback, because the test suite will wait until it's called. Use done when you can't easily return a Promise. In your case the second test will be infinite, because you define done, which you never actually call.
I'm doing some unit tests using Node.js and I want to fail a test like this:
doSomething()
.then(...)
.catch(ex => {
// I want to make sure the test fails here
});
I'm using Assert, so I found Assert.Fails. The problem is that fails expects actual and expected, which I don't have. The Node documentation doesn't say anything about them being required, but the Chai documentation, which is Node compliant, say they are.
How should I fail a test on the catch of a promise?
You can use a dedicated spies library, like Sinon, or you can implement a simple spy yourself.
function Spy(f) {
const self = function() {
self.called = true;
};
self.called = false;
return self;
}
The spy is just a wrapper function which records data about how the function has been called.
const catchHandler = ex => ...;
const catchSpy = Spy(catchHandler);
doSomething()
.then(...)
.catch(catchSpy)
.finally(() => {
assert.ok(catchSpy.called === false);
});
The basic principle is to spy on the catch callback, then use the finally clause of your promise to make sure that the spy hasn't been called.
If you will use mocha, then elegant way would be as following:
describe('Test', () => {
it('first', (done) => {
doSomething()
.then(...)
.catch(done)
})
})
If your Promise fail, done method will be invoked with the thrown exception as a parameter, so code above is equivalent to
catch(ex => done(ex))
In mocha invoking done() with the parameter fails the test.
Have you considered Assert.Ok(false, message)? It's more terse.
Assert.fail is looking to do a comparison and display additional info.
One of the things that I find frustrating about Mocha is that when tests fail, they don't give the actual error message of the failing line, Instead, they just end with Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
Take this test for example:
describe("myTest", function() {
it("should return valid JSON.", function(done) {
api.myCall("valid value").then(function(result) {
console.log(result);
var resultObj = JSON.parse(result);
assert.isFalse(resultObj.hasOwnProperty("error"), "result has an error");
done();
});
});
});
The output is:
myTest
{"error":null,"status":403}
1) should return valid JSON.
0 passing (2s)
1 failing
1) myTest should return valid JSON.:
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
The assert.isFalse is failing, but the message that should be displayed ("result has an error") isn't displayed. In fact, processing seems to stop right there because done() is never called. Take that line out and the test passes because done() is called.
So, what am I missing? Why do Mocha tests behave this way? The actual test library I'm using is:
var assert = require("chai").assert;
Does anyone know what I'm doing wrong or why this behaves this way?
It looks like your API is using promises. Before trying anything else, I would suggest checking what the documentation of the API says about promises and how to deal with unhandled exceptions because this may be what is happening here. Some promise implementations require that you call .done() at the end of your call chain to ensure that uncaught exceptions are going to be processed. Some require that some global promise setting be properly configured. The Bluebird documentation gives a good discussion of the issues.
Mocha is capable of handling uncaught exceptions in run-of-the-mill code:
var chai = require("chai");
var assert = chai.assert;
chai.config.includeStack = true;
describe("foo", function() {
it("let the exception be caught by Mocha", function(done) {
setTimeout(function () {
assert.isFalse(true, "foo");
done();
}, 1000);
});
});
This will result in the output:
foo
1) let the exception be caught by Mocha
0 passing (1s)
1 failing
1) foo let the exception be caught by Mocha:
Uncaught AssertionError: foo: expected true to be false
at Assertion.<anonymous> (/tmp/t7/node_modules/chai/lib/chai/core/assertions.js:286:10)
at Assertion.Object.defineProperty.get (/tmp/t7/node_modules/chai/lib/chai/utils/addProperty.js:35:29)
at Function.assert.isFalse (/tmp/t7/node_modules/chai/lib/chai/interface/assert.js:297:31)
at null._onTimeout (/tmp/t7/test.js:8:20)
at Timer.listOnTimeout (timers.js:119:15)
I've encountered the same in my code, using Q for promises.
What happened was:
The assertion inside the then block failed.
The rest of the then block, including the done() statement, was not executed.
Q went looking for a catch block, which wasn't there.
This led to a 'hanging' promise, and thus to a Mocha 2000 ms timeout.
I worked around it by doing something like this:
describe("myTest", function() {
it("should return valid JSON.", function(done) {
api.myCall("valid value").then(function(result) {
console.log(result);
var resultObj = JSON.parse(result);
assert.isFalse(resultObj.hasOwnProperty("error"), "result has an error");
done();
})
.catch(function(err) {
console.error(err);
done(err);
});
});
});