I'm running some asynchronous tests with Mocha, but some future tests can't be executed until previous ones are completed. For this, I can simply use done() callback to run them synchronously:
describe('first operations', function() {
it('should do something', function(done) {
longOperation(function(err) {
done();
});
});
it('should do something', function(done) {
longOperation(function(err) {
done();
});
});
});
describe('second operations', function() {
it('should do something', function(done) {
longOperation(function(err) {
done();
});
});
it('should do something', function(done) {
longOperation(function(err) {
done();
});
});
});
Doing so though, slows down the entire operation because I'm stuck running each it() block synchronously. I'd like to run the inner tests asynchronously, and each describe block synchronously, but the done() callback doesn't seem to work that way (at least, using async).
Am I doing something wrong, or is that simply not supported? If not, is there a way I can get around this to accomplish my goal?
describe('first operations', function(done) {
async.parallel([
function(callback) {
it('should do something', function() {
longOperation(function(err) {
callback();
});
});
},
function(callback) {
it('should do something', function() {
longOperation(function(err) {
callback();
});
});
}
], function(err) {
done();
});
});
describe('second operations', function(done) {
async.parallel([
function(callback) {
it('should do something', function() {
longOperation(function(err) {
callback();
});
});
},
function(callback) {
it('should do something', function() {
longOperation(function(err) {
callback();
});
});
}
], function(err) {
done();
});
});
mocha-parallel-tests seems to have been created to fill this need. Here's a description from the project site:
Normally tests written with mocha run sequentially. This happens so because each test suite should not depend on another. But if you are running tests which take a lot of time (for example tests with Selenium Webdriver) waiting for so much time is impossible.
I believe this will run all tests in parallel though. To limit the describe blocks to run sequentially you could put them in individual files and then run mocha-parallel-tests separately on these files.
context.describe() doesn't seem to be an instance of Runnable. context.it(), on the other hand seems to be creating an instance of Runnable. It looks like only instances of Runnable receive a callback.
So no, it looks like you cannot run describe() blocks serially while running enclosed it() blocks asynchronously.
Related
I try to test a middleware in mocha.
The problem is, that all "it" calls waiting for the previous one to finish, before executing their callback.
it(`should trigger pre hook 1`, (done) => {
use((next) => {
setTimeout(() => {
done();
next();
}, 1000);
});
});
it(`should trigger pre hook 2`, (done) => {
use((next) => {
setTimeout(() => {
done();
next();
}, 1000);
});
});
start(() => {
// middleware done
});
The second it(...) waits for the first to complete.
And thats exactly the problem, since the second use(...) is not called before i fire the start(...) function, so it never gets executed and the test fails.
How can i tell mocha to execute all "it" callbacks and wait not for the previous one to complete (or fails)?
Try a spy instead of done so you can layout the tests as needed without relying on mocha to control the flow.
describe('middleware', function(){
// initialise use/start for this test
const m1 = sinon.spy()
const m2 = sinon.spy()
use((next) => {
setTimeout(() => {
m1();
next();
}, 1000);
});
use((next) => {
setTimeout(() => {
m2();
next();
}, 1000);
});
it(`should process a request through middleware`, function(done){
start(() => {
// middleware done
expect(m1.calledOnce, `m1`).to.equal(true)
expect(m2.calledOnce, `m2`).to.equal(true)
done()
});
})
})
The spy will also let you check on more complex call scenarios in the middleware when you have functional code in there.
I want to run some tests whose settings need to be downloaded first via a HTTP GET.
My download is successful but my test does not run when it's inside the request callback. I know it's not the best structure but I'd also like to know why this is not working.
describe('test', () => {
request.get({
url: 'https://google.com',
}, (err, status, body) => {
// The content is downloaded successfully.
console.log(body);
// This test never runs, why?
it('should be able to run inside a request.get', () => {
});
});
});
I know this code works but I would still like to know why the previous example does not.
describe('test', () => {
it('should be able to run inside a request.get', () => {
request.get({
url: 'https://google.com',
}, (err, status, body) => {
console.log(body);
});
});
});
EDIT: The suggestion provided by Jankapunkt's comment works: Moving the 'it' and 'describe' together allows for a successful download and test run.
request.get({
url: 'https://google.com',
}, (err, status, body) => {
// The content is downloaded successfully.
console.log(body);
// The describe and it are within the same closure.
describe('test', () => {
// This test runs successfully.
it('should be able to run inside a request.get', () => {
});
});
});
Old subject but I did like this for a Mock mongo connection:
const manager = require('./manager')
const assert = require('assert')
const MongoClient = require('./MockMongo')
let conn
describe('test execution', function() {
it('db connection', function (done) {
MongoClient.connect('test url')
.then((db) => {
conn = db
done()
})
})
it('test 1', function (done) {
manager.methodOfManager(conn, 'param1', 'param2')
.then(r => {
assert.equal(r.result, 'ok')
done()
})
.catch(err => {
console.log(err.message)
done(err)
})
})
})
It will print:
test execution
✓ db connection (5ms)
✓ Test 1 (sepa) (125ms)
2 passing (0s)
Approach 1: Tests inside your GET result
Use describe() around the it() within the callback function and avoid arrow functions like the following:
it("...", () => {});
It is discouraged in mocha when you have changing contexts.
Instead use
it("...", function(){});
and use .bind , done() or promises when required in async tests.
Putting this together into your code example, you may find your code to be similar to the following:
request.get({
url: 'https://google.com',
}, (err, status, body) => {
// The content is downloaded successfully.
describe('test', function() {
it('should be able to run inside a request.get', function() {
assert.isDefined(status);
assert.isDefined(body);
//...and so on
});
});
});
By the way - it is only a bad structure if for your approach is a better structure available.
Approach 2 - Wrap request.get in your unit
This is (in my opinion) the better and more mocha-like approach, where you execute the request inside the test and use the done() callback to notify mocha, that you are done:
describe('test', function() {
let request;
beforeEach(function(){
// request = ...
});
it('should be able to get a request result', function(done) {
request.get({
url: 'https://google.com',
}, (err, status, body) => {
assert.isDefined(status);
assert.isDefined(body);
//...and so on
// at the end call done
done();
});
});
You can ensure that request is initialized each test as a new fresh instance by using the beforeEach hook.
I\m trying to write unit tests for some code I wrote, the problem I'm running into is I expect my mock callback to be called after executing the function but my test fails as it is never called.
describe("Asynchronous specs", function() {
var mockNext;
beforeEach(function() {
mockNext = jasmine.createSpy('mockNext');
var res;
parallelRequests.APICall(testObject[0], null, mockNext);
});
it("callback spy should be called", function () {
expect(mockNext).toHaveBeenCalled();
});
});
The function being tested is quite simple:
function APICall(options, res, next) {
request(options, callback);
function callback(error, response, body) {
if (error) {
if (error.code === 'ETIMEDOUT') {
return logger.error('request timed out: ', error);
next(error);
}
logger.error('request failed: ', error);
next(error);
}
next(null);
}
}
The issue I suspect is jasmine testing the expectation before the mock callback is executed in API Call due to request's async nature. I've tried using what others suggest of using done() and flags but with no luck. Would appreciate some guidance in this matter.
Your beforeEach code is asynchronous. You have to tell yasmin when your beforeEach logic is done. You can solve this by the callback method done, that is passed to each test. Try this:
describe("Asynchronous specs", function() {
var mockNext;
beforeEach(function(done) {
parallelRequests.APICall(testObject[0], null, function(){
mockNext = jasmine.createSpy('mockNext');
mockNext();
done();
});
});
it("callback spy should be called", function () {
expect(mockNext).toHaveBeenCalled();
});
});
This is more or less a duplicate of supertest test express middleware
but after a year, I figured I'd start a new question.
var express = require('express');
var request = require('supertest');
var app1 = express();
app1.get('/myapp', function (req, res) {
res.send(200, { name: 'myapp' });
});
request = request(app1);
it('should fail', function () {
request
.get('/hahahahahahahaha')
.expect(123);
});
As far as I can tell, that will always erroneously pass. The fact that the path is wrong and is expecting a different status code doesn't matter.
And - more generically (without Express), it looks like this always passes, also:
it('should fail', function () {
request('http://thisdoesnotexist.mydomain')
.get()
.expect(200);
});
This doesn't work either:
it('should fail', function () {
request('http://thisdoesnotexist.mydomain')
.get()
.expect(200)
.end(function (err, res) {
if (err) {
throw err;
}
});
});
Any thought as to why this happens, or how to actually test such a scenario?
With supertest you need to terminate your chain somehow.
expect will take a finished callback as the second parameter, and you can use the build in mocha callback for this. Like so:
describe('potato', function() {
it('should fail', function(done) {
request
.get('/hahahahahahahaha')
.expect(123, done);
});
});
Specifying a done option like this will instruct mocha to wait until it's heard back from you before proceeding to the next test.
The difference is the parameter: done
describe('XXX', function() {
it('XXX', function() {
// always passing
})
})
describe('YYY', function(done) {
it('YYY', function() {
// always passing
})
})
describe('ZZZ', function() {
it('ZZZ', function(done) {
// normal
})
})
I'm trying to mock the function fs.readdir for my tests.
At first I've tried to use sinon because this is a very good framework for this, but is hasn't worked.
stub(fs, 'readdir').yieldsTo('callback', { error: null, files: ['index.md', 'page1.md', 'page2.md'] });
My second attempt was to mock the function with a self-replaced function. But it also doesn't works.
beforeEach(function () {
original = fs.readdir;
fs.readdir = function (path, callback) {
callback(null, ['/content/index.md', '/content/page1.md', '/content/page2.md']);
};
});
afterEach(function () {
fs.readdir = original;
});
Can anybody tell me why both doesn't works? Thanks!
Update - This also doesn't works:
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
Update2:
My last attempt to mock the readdir function is working, when I'm trying to call this function directly in my test. But not when I'm calling the mocked function in another module.
I've found the reason for my problem. I've created the mock in my test class tried to test my rest api with supertest. The problem was that the test was executed in another process as the process in that my webserver runs. I've created the express-app in my test class and the test is now green.
this is test
describe('When user wants to list all existing pages', function () {
var sandbox;
var app = express();
beforeEach(function (done) {
sandbox = sinon.sandbox.create(); // #deprecated — Since 5.0, use sinon.createSandbox instead
app.get('/api/pages', pagesRoute);
done();
});
afterEach(function (done) {
sandbox.restore();
done();
});
it('should return a list of the pages with their titles except the index page', function (done) {
sandbox.stub(fs, 'readdir', function (path, callback) {
callback(null, ['index.md', 'page1.md', 'page2.md']);
});
request(app).get('/api/pages')
.expect('Content-Type', "application/json")
.expect(200)
.end(function (err, res) {
if (err) {
return done(err);
}
var pages = res.body;
should.exists(pages);
pages.length.should.equal(2);
done();
});
});
});