I am using nock library to stub my http calls.
Different test files require('nock') and do their stubbing.
If each test is run separately, all is passing.
But if all tests run together, later tests fail because instead of nock, actual request was made.
Consider below code snippet for example. It has two different describe blocks, each with multiple test cases. If I run this file node node_modules/mocha/bin/_mocha test.js then the first two tests will pass, but the third test (in different describe block) would fail because it would actually call the google URL.
/* eslint-env mocha */
let expect = require('chai').expect
let nock = require('nock')
let request = require('request')
let url = 'http://localhost:7295'
describe('Test A', function () {
after(function () {
nock.restore()
nock.cleanAll()
})
it('test 1', function (done) {
nock(url)
.post('/path1')
.reply(200, 'input_stream1')
request.post(url + '/path1', function (error, response, body) {
expect(body).to.equal('input_stream1')
done()
})
})
it('test 2', function (done) {
nock(url)
.post('/path2')
.reply(200, 'input_stream2')
request.post(url + '/path2', function (error, response, body) {
expect(body).to.equal('input_stream2')
done()
})
})
})
// TESTS IN THIS BLOCK WOULD FAIL!!!
describe('Test B', function () {
after(function () {
nock.restore()
nock.cleanAll()
})
it('test 3', function (done) {
nock('http://google.com')
.post('/path3')
.reply(200, 'input_stream3')
request.post('http://google.com' + '/path3', function (error, response, body) {
expect(body).to.equal('input_stream3')
done()
})
})
})
Funny thing is, if I do console.log(nock.activeMocks()), then I can see that nock did register the URL to mock.
[ 'POST http://google.com:80/path3' ]
As discussed in this Github Issue, nock.restore() removes the http interceptor itself. When you run nock.isActive() after calling nock.restore() it will return false. So you need to run nock.activate() before using it again.
Solution 1:
Remove nock.restore().
Solution 2:
Have this before() method in your test.
before(function (done) {
if (!nock.isActive()) nock.activate()
done()
})
Related
I'm trying to run several integration tests with some shared context. The context being shared is a single express application, and I'm trying to share it across suites / files because it takes a few seconds to spin up.
I got it to work by instantiating a "runner" mocha test suite, that would have test functions that would just require each test file as needed, and this was working well (a side effect is that the test requiring the child test file would finish as "success" before any of the tests inside the file would actually run, but this was a minor issue)
// test-runner.js:
describe('Integration tests', function () {
let app
let log
this.timeout(300000) // 5 mins
before(function (done) {
app = require('../app')
app.initialize()
.then(() => {
done()
})
.catch(err => {
log.error(err)
done(err)
})
})
it('Running api tests...', (done) => {
require('./integration/api.test')(app)
done()
})
// ./integration/api.test.js:
module.exports = (app) => {
let api = supertest.agent(app)
.set('Content-Type', 'application/json')
describe('Authorization', () => {
describe('Trying to access authorization sections', () => {
it('should be denied for /home', async () => {
await api.get(`${baseUrl}/home`)
.expect(STATUS_CODE.UNAUTHORIZED)
})
...
The Problem:
I want to signal the test runner that all of the tests in the imported suite have finished, so I can call shutdown logic in the test runner and end the test cleanly. In standard test functions, you can pass a done function to signal that the code in the test is complete, so I wrapped each of the child tests in a describe block to use the after hook to signal that the whole test module was done:
// test-runner.js:
describe('Integration tests', function () {
let app
let log
this.timeout(300000) // 5 mins
before(function (done) {
app = require('../app')
app.initialize()
.then(() => {
done()
})
.catch(err => {
log.error(err)
done(err)
})
})
it('Running api tests...', (done) => {
require('./integration/api.test')(app, done)
})
// ./integration/api.test.js:
module.exports = (app, done) => {
let api = supertest.agent(app)
.set('Content-Type', 'application/json')
describe('All api tests', () => {
let api
before(() => {
api = supertest.agent(app)
.set('Content-Type', 'application/json')
})
after(() => {
done() // should be calling the done function passed in by test runner
})
describe('Authorization', () => {
describe('Trying to access authorization sections', () => {
it('should be denied for /home', async () => {
await api.get(`${baseUrl}/home`)
.expect(STATUS_CODE.UNAUTHORIZED)
})
...
but when I do this, the test suites just don't run. The default timeout will just expire, and if I set a higher timeout, it just sits there (waiting for the longer timeout). If I hook into a debug session, then the test exits immediately, and the after hook (and before!) never get called.
I'm open to other ideas on how to do this as well, but I haven't found any good solutions that that allow sharing some context between tests, while having them broken into different files.
In my mocha-test suite, I want to test a functionality which makes a asynchronous call behind the scene. How can I wait until the asynchronous call has finished ?
For example, I make two back to back post calls. The first post call also makes an asynchronous call internally, until that asynchronous operation is complete the second post call won't pass.
I need either of below:
1) to put a delay between the two post calls so that to make sure the asynchronous part in the first post is complete.
2) to make the second post call repetitively until it passes.
3) or how to test out the asynchronous call through mocha-chai ?
Below is the example:
describe('Back to back post calls with asynchronous operation', ()=> {
it('1st and 2nd post', (done) => {
chai.request(server)
.post('/thisis/1st_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
/* HERE I Need A Delay or a way to call
the below post call may be for 5 times */
chai.request(server)
.post('/thisis/second_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
});
done();
});
});
});
Is there a way to handle this ? Please help.
Thanks.
In order to test an asynchronous function with mocha you have the following possibilities
use done only after the last callback in your sequence was executed
it('1st and 2nd post', (done) => {
chai.request(server)
.post('/thisis/1st_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
/* HERE I Need A Delay or a way to call
the below post call may be for 5 times */
chai.request(server)
.post('/thisis/second_post')
.send()
.end((err, res) => {
expect(res.statusCode).to.equal(200);
//call done only after the last callback was executed
done();
});
});
});
use done callback with promises
describe('test', () => {
it('should do something async', (done) => {
firstAsyncCall
.then(() => {
secondAsyncCall()
.then(() => {
done() // call done when you finished your calls
}
})
});
})
After a proper refactor you will get something like
describe('test', () => {
it('should do something async', (done) => {
firstAsyncCall()
.then(secondAsyncCall())
.then(() => {
// do your assertions
done()
})
.catch(done)
})
})
use async await, much cleaner
describe('test', () => {
it('should do something async', async () => {
const first = await firstAsyncCall()
const second = await secondAsyncCall()
// do your assertion, no done needed
});
})
An other moment to keep in mind is the --timeout argument when running mocha tests. By default mocha is waiting 2000 miliseconds, you should specify a larger amount when the server is responding slower.
mocha --timeout 10000
I am using express module and below is the code of app.js
app.post('/test_url', function(request, response){
seneca.client({type: 'http',port: '3000',host: 'localhost',protocol: 'http'}).act({role: 'sample_role', cmd: 'save',firstname: request.params.firstname}, function (err, result) {
console.log("Inside Seneca act");
response.json(result);
})
});
Below is the test file where I am writing the test case for above code.
describe("POST /test_url/:firstname", function() {
it("should return status code 200", function(done) {
<b>//here I want to mock the call for seneca.client so that I can test if the call has been made with the required parameters.</b>
<b>//Also I would like to use the above mock object to further mock the call for act so that I can check if the act method has been called with the required parameters.'</b>
//Main purpose behind doing so is that I do not want the seneca methods to get actually called, and only want to test if the call has been made.
request.post("http://localhost:3000/test_url/sara", function(error, response, body) {
//some verification method on the mock object so as to confirm that both the calls i.e 'seneca.client' and 'seneca.client().act' have been called with the appropriate parameters
expect(body).toContain("success");
done();
});
});
});
I tried to mock the seneca calls using jasmine spy and sinon but still the call was actually being going to the method and the the callback function was also invoked resulting in the console.log("Inside Seneca act"); being called, which is not what I expect.
describe("POST /test_url/:firstname", function() {
it("should return status code 200", function(done) {
var senecaCall = sinon.stub(seneca, 'client');
//or spyOn(seneca, "client");
request.post("http://localhost:3000/test_url/sara", function(error, response, body) {
expect(body).toContain("success");
done();
});
});
});
describe("Company Controller", function() {
var apiUrl;
beforeEach(function(done) {
apiUrl = "http://localhost:3001";
done();
});
it('should register a client without error and return an API key', function(done) {
request({
uri: apiUrl + '/api/v1/company',
method: 'POST',
json: true,
form: {
name: 'My Company'
}
}, function(err, res, body) {
should.not.exist(err);
res.statusCode.should.eql(200);
body.status.should.eql('ok');
should.exist(body.company.api_key);
done();
});
});
it('should generate a new API key for a company', function(done) {
// NEED THE client_id generated in the previous test
});
after(function(done) {
Company.remove().exec();
done();
});
});
How do I get the client_id in the next test?
Generally speaking, making tests with side effects is a brittle practice. Do this often enough, you'll start to encounter some very difficult-to-debug errors where your test suite fails even though every test runs in isolation, and the error messages won't be any help. Ideally every test should "leave the campground" in the same state that it found it.
If you're really insistent on doing this, you could of course set a global variable. Some other options include:
Merging the two tests. Yes, this violates a principle of Single-Assertion-Per-Test that some people hold, but I think the Avoid-Side-Effects principle trumps that one.
Put the registration in the beforeEach function. Yes, by doing this you'll be registering multiple clients per test suite run. This is still my preferred approach.
You should use a stub. For example sinonjs. This way you use a fake function to test another. You don't need to use beforeEach and afterEach if you only need to stub the function once you can define it inside the it function.
describe("Company controller", function() {
describe("dependantFunction()", function() {
var stub;
beforeEach(function() {
stub = sinon.stub(module, 'targetFunction');
stub.onCall(0).callsArgWith(1, ...some valid results...);
stub.onCall(1).callsArgWith(1, ...some expected error...);
stub.throws();
});
afterEach(function() {
stub.restore();
});
it("should do something", function() {
var result;
module.targetfunction(null, result);
dependantfunction(result);
....your tests here....
});
it("should do something else", function() {
...
});
});
});
In my node application I'm using mocha to test my code. While calling many asynchronous functions using mocha, I'm getting timeout error (Error: timeout of 2000ms exceeded.). How can I resolve this?
var module = require('../lib/myModule');
var should = require('chai').should();
describe('Testing Module', function() {
it('Save Data', function(done) {
this.timeout(15000);
var data = {
a: 'aa',
b: 'bb'
};
module.save(data, function(err, res) {
should.not.exist(err);
done();
});
});
it('Get Data By Id', function(done) {
var id = "28ca9";
module.get(id, function(err, res) {
console.log(res);
should.not.exist(err);
done();
});
});
});
You can either set the timeout when running your test:
mocha --timeout 15000
Or you can set the timeout for each suite or each test programmatically:
describe('...', function(){
this.timeout(15000);
it('...', function(done){
this.timeout(15000);
setTimeout(done, 15000);
});
});
For more info see the docs.
I find that the "solution" of just increasing the timeouts obscures what's really going on here, which is either
Your code and/or network calls are way too slow (should be sub 100 ms for a good user experience)
The assertions (tests) are failing and something is swallowing the errors before Mocha is able to act on them.
You usually encounter #2 when Mocha doesn't receive assertion errors from a callback. This is caused by some other code swallowing the exception further up the stack. The right way of dealing with this is to fix the code and not swallow the error.
When external code swallows your errors
In case it's a library function that you are unable to modify, you need to catch the assertion error and pass it onto Mocha yourself. You do this by wrapping your assertion callback in a try/catch block and pass any exceptions to the done handler.
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(function (err, result) {
try { // boilerplate to be able to get the assert failures
assert.ok(true);
assert.equal(result, 'bar');
done();
} catch (error) {
done(error);
}
});
});
This boilerplate can of course be extracted into some utility function to make the test a little more pleasing to the eye:
it('should not fail', function (done) { // Pass reference here!
i_swallow_errors(handleError(done, function (err, result) {
assert.equal(result, 'bar');
}));
});
// reusable boilerplate to be able to get the assert failures
function handleError(done, fn) {
try {
fn();
done();
} catch (error) {
done(error);
}
}
Speeding up network tests
Other than that I suggest you pick up the advice on starting to use test stubs for network calls to make tests pass without having to rely on a functioning network. Using Mocha, Chai and Sinon the tests might look something like this
describe('api tests normally involving network calls', function() {
beforeEach: function () {
this.xhr = sinon.useFakeXMLHttpRequest();
var requests = this.requests = [];
this.xhr.onCreate = function (xhr) {
requests.push(xhr);
};
},
afterEach: function () {
this.xhr.restore();
}
it("should fetch comments from server", function () {
var callback = sinon.spy();
myLib.getCommentsFor("/some/article", callback);
assertEquals(1, this.requests.length);
this.requests[0].respond(200, { "Content-Type": "application/json" },
'[{ "id": 12, "comment": "Hey there" }]');
expect(callback.calledWith([{ id: 12, comment: "Hey there" }])).to.be.true;
});
});
See Sinon's nise docs for more info.
If you are using arrow functions:
it('should do something', async () => {
// do your testing
}).timeout(15000)
A little late but someone can use this in future...You can increase your test timeout by updating scripts in your package.json with the following:
"scripts": {
"test": "test --timeout 10000" //Adjust to a value you need
}
Run your tests using the command test
For me the problem was actually the describe function,
which when provided an arrow function, causes mocha to miss the
timeout, and behave not consistently. (Using ES6)
since no promise was rejected I was getting this error all the time for different tests that were failing inside the describe block
so this how it looks when not working properly:
describe('test', () => {
assert(...)
})
and this works using the anonymous function
describe('test', function() {
assert(...)
})
Hope it helps someone, my configuration for the above:
(nodejs: 8.4.0, npm: 5.3.0, mocha: 3.3.0)
My issue was not sending the response back, so it was hanging. If you are using express make sure that res.send(data), res.json(data) or whatever the api method you wanna use is executed for the route you are testing.
Make sure to resolve/reject the promises used in the test cases, be it spies or stubs make sure they resolve/reject.