It seems that you could put "stuff" inside describe but outside it:
describe('1', function() {
var foo = 'bar';
console.log(foo);
it('1a', function(done) {
expect(true).toBe(true);
done()
});
it('1b', function(done) {
expect(true).toBe(true);
done();
});
});
And it seems that you could put the it blocks inside of a function that is called:
describe('1', function() {
(function() {
it('1a', function(done) {
expect(true).toBe(true);
done()
});
it('1b', function(done) {
expect(true).toBe(true);
done();
});
})()
});
In both cases the output is:
~/code/study-buddies $ jasmine-node server
bar
..
Finished in 0.005 seconds
2 tests, 2 assertions, 0 failures, 0 skipped
But when I try to put my it blocks inside of a Node request callback, it doesn't seem to run the tests:
describe('1', function cb1() {
var foo = 'bar';
console.log(foo);
var request = require('request');
request.get('http://localhost:3000/api/posts', function() {
console.log('here');
it('1a', function cb1a(done) {
console.log('1a');
expect(true).toBe(true);
done();
});
it('1b', function cb1b(done) {
console.log('1b');
expect(true).toBe(true);
done();
});
});
});
Output:
~/code/study-buddies $ jasmine-node server
bar
Finished in 0.007 seconds
0 tests, 0 assertions, 0 failures, 0 skipped
here
When I run my real code:
var request = require('request');
var base_url = 'http://localhost:3000/api/posts';
describe('Posts', function() {
var post;
beforeEach(function(done) {
var options = {
url: base_url,
method: 'POST',
json: {
title: 'one'
}
};
request(options, function(err, response, body) {
if (err) console.log('ERROR IN THE SET UP.');
else { post = body; }
done();
});
});
afterEach(function(done) {
request.del(base_url+'/'+post._id, function(err, response, body) {
if (err) console.log('ERROR IN THE TEAR DOWN.');
else { post = null; }
});
done();
});
describe('GET /', function() {
it('returns a status code of 200', function(done) {
request.get(base_url, function(err, response, body) {
expect(response.statusCode).toBe(200);
done();
});
});
it('returns the right posts', function(done) {
request.get(base_url, function(err, response, body) {
expect(JSON.parse(body)).toEqual([post]);
done();
});
});
});
describe('GET /:id', function() {
it('returns a status code of 200', function(done) {
request.get(base_url+'/'+post._id, function(err, response, body) {
expect(response.statusCode).toBe(200);
done();
});
});
it('returns the right post', function(done) {
request.get(base_url+'/'+post._id, function(err, response, body) {
expect(JSON.parse(body)).toEqual(post);
done();
});
});
});
describe('POST /', function() {
var options = {
url: base_url,
method: 'POST',
json: {
title: 'two'
}
};
request(options, function(err, response, body) {
it('returns a status code of 201', function(done) {
expect(response.statusCode).toBe(201);
done();
});
it('returns the created Post', function(done) {
expect(body).toEqual({title: 'two'});
done();
});
});
});
describe('PUT /:id', function() {
});
describe('DELETE /:id', function() {
});
});
I get this no output:
~/code/study-buddies $ jasmine-node server
~/code/study-buddies $
Why is this?
Note: I'm trying to nest the it blocks under the request because both it blocks are making the same request, so I want to be DRY.
There are two steps here:
Collect tests (calls to it()).
Execute collected tests.
In your first example your tests are defined, while handling step 1. In your second example (with callback) you calls to it are executed during step 2 and therefore they are not handled by Jasmine.
How to handle
You need either define separate tests or use only calls to expect() inside your callback without calls to it().
Usually you want to define separate test if you need to send request to the same route, but with different parameters. Typical example is to test API behavior with valid and invalid data:
describe('POST /users/', function() {
it('should create user', function (done) {
request.post('/users/', { /* valid data */ }, function () { /* expect clauses */ });
});
it('should respond with errors given invalid data', function (done) {
request.post('/users/', { /* invalid data */ }, function () { /* expect clauses */ });
});
});
You want to use multiple expect() statements inside single test, when you want to test different parts of a single request. For example check response code and values of few parameters:
describe('GET /users/{id}/', function() {
it('should return user', function (done) {
request.get('/users/14/', function (err, response, body) {
expect(response.code).toBe(200);
expect(body.username).toBe('test');
expect(body.email).toBe('test#example.com');
});
});
});
Obviously there are some in-between cases and that's up to you to decide whether each concrete case is important enough to be put into separate test (and duplicate the same request) or can be handle by additional expect() statements inside single test.
In depth explanation
This requires some background knowledge from reader:
understand difference between synchronous & asynchronous
know what is event loop in Node.js and how it works
understand that function, which accepts another function is not necessary asynchronous (uses event loop for callback) (like forEach for example)
Some facts before continue:
Jasmine expects all test cases to be defined synchronously. After call to main describe() in file is completed it starts execution of collected tests.
Calls to describe() and it() are synchronous, while execution of HTTP requests are asynchronous (uses event loop).
describe() creates a namespace for tests and synchronously calls function provided as second argument. it() adds it's second argument as a test to current namespace.
Let's trace your first case:
Call to describe() creates namespace 1 and synchronously executes cb1().
Call to it() synchronously adds test 1a.
Call to it() synchronously adds test 1b.
Execution of cb1() is completed, as well as test collection step.
Start execution of collected tests.
Execute tests.
Print results.
Exit program.
Addition of IIFE doesn't change anything, since it's just calls itself synchronously.
Let's trace your second case:
Call to describe() creates namespace 1 and synchronously executes cb1().
Send request, put callback to the event loop queue (remember HTTP requests are asynchronous?) and continue execution on the next line.
Execution of cb1() is completed, as well as test collection step.
Start execution of collected tests.
Since there are not tests collected, print 0 tests executed.
Execution of the current call stack is finished. So take next function from the event loop queue, which is request callback.
Call to it() tries to add test 1a, but execution of tests is already completed.
Exit program.
At this point it should be clear that you can't and shouldn't define tests inside asynchronous callbacks. So your third example should never be written. And question why it completes without any output should never be asked.
But just out of the interest I looked at the Jasmine source code and did some debugging to see what actually happened. Here you go.
You probably noticed that functions describe() and it() aren't imported by you, but provided by Jasmine itself. Besides importing these functions for you Jasmine also provides some internal state used by it() and describe() to collect tests. While importing tests this internal state is set, but while running it's not.
When you call it() from the request callback you do this at the stage of running tests and internal set is not set. So it fails with Error: jasmine.Suite() required error. This error causes Jasmine to exit immediately without any output.
You will probably ask why does it print results in the second example then? That's easy: in your second example you don't have any other tests, so at the moment of call to it() results are already printed. You can check it by adding another console.log() call between calls to it() (it will never be printed).
Related
I'm looking to create some jasmine specs for a piece of code that has async functionality.
In the jasmine docs it shows the example:
it("takes a long time", function(done) {
setTimeout(function() {
done();
}, 9000);
});
Using the done function and a setTimeout, my issue with this is setTimout could be fragile i.e. delays in test runs in enviros
is there an alternative solution to such tests where I don't have to use a timeout?
Thanks in advance
In this example setTimeout is actually the function being tested. It's used as a representative example of an asynchronous function. The key point is that you must explicitly call done() when your test is complete. Your code should look something like:
it("takes a long time", function(done) {
myMethod('foo', 'bar', function callback() {
assert(...)
done();
}); // callback-style
}
it("takes a long time", function(done) {
myMethod('foo', 'bar').then(function() {
assert(...)
done();
}); // promise-style
});
it("takes a long time", async function(done) {
await myMethod('foo', 'bar')
assert(...)
done()
});
The documented function is intended to illustrate using the done callback following a long-running method, and should not be used for actual tests.
Normally, you would expect a long running function to be supplied with a callback in which you would call the done function. For example, you could write a unit test involving a file that took a long time to write data:
it("writes a lot of data", function(done) {
var fd = 999; // Obtain a file descriptor in some way...
fs.write(fd, veryLongString, function (err, written, string) {
// Carry out verification here, after the file has been written
done();
});
Again, this is only illustrative, as you would generally not want to write to a file within the body of a unit test. But the idea is that you can call done after some long-running operation.
I've read some tutorials online for the Promises method but I'm still a bit confused.
I have a Node app.js which performs several functions including connecting to a db.
db.connect(function(err) {
setupServer();
if(err) {
logger.raiseAlarmFatal(logger.alarmId.INIT,null,'An error occurred while connecting to db.', err);
return;
}
Now I have written a mocha unit test suite, which encapsulates this app and performs several request calls to it. In some cases what occurs is that the the test initializes without confirmation that the db has successfully connected i.e: setupServer() has been performed.
How would I implement the promises method to this bit of asynchronous code, and if not promises, what should I use ? I have already tried event emitter but this still does not satisfy all the requirements and causes failures during cleanup.
If you're using mocha, you should use asynchronous code approach. This way you can instruct mocha to wait for you to call done function before it goes on with the rest.
This would get you started:
describe('my test', function() {
before(function(done) {
db.connect(function(err) {
setupServer(done);
});
})
it('should do some testing', function() {
// This test is run AFTER 'before' function has finished
// i.e. after setupServer has called done function
});
});
assuming that your setupServer calls the done function when it's done:
function setupServer(done) {
// do what I need to do
done();
}
You will need to use Promise inside the body of function that has async work. For your case, I think that is setupServer() which you said contains ajax requests.
conts setupServer = () => {
return new Promise((resolve, reject) => {
//async work
//get requests and post requests
if (true)
resolve(result); //call this when you are sure all work including async has been successfully completed.
else
reject(error); //call this when there has been an error
});
}
setupServer().then(result => {
//...
//this will run when promise is resolved
}, error => {
//...
//this will run when promise is rejected
});
For further reading:
Promise - MDN
I want to test some promises with jasmine node. However, the test runs but it says that there are 0 assertions. This is my code, is there something wrong? The then part is successfully called, so if I have a console.log there, it gets called. If I have the code test a http request, on the success, the assertion is correctly interpretated.
describe('Unit tests', function () {
it("contains spec with an expectation", function() {
service.getAllClients().then(function (res) {
expect("hello world").toEqual("hello world");
done();
}).catch(function (err) {
fail();
});
});
});
You need to specify the done argument to the callback you pass to it, so Jasmine knows you are testing something asynchronously:
it("contains spec with an expectation", function(done) {
// ...
When you include that parameter, Jasmine will wait for a while for you to call done so it knows when you're done.
done();
Secondly, in an asynchronous test, it probably is better to fail with a call of done.fail:
done.fail();
Before testing a certain feature of my Meteor application with Jasmine I have to prepare different things for the tests. Therefore I use beforeAll blocks.
Reset the database
Create a lecture in the database
Create a question in the database
Go to the page of the just created lecture
Wait for the Router to finish routing
These asynchronous tasks have to run in series. I can not first go to the lecture page and then create it in the database. Sadly beforeAll blocks in Jasmine will not automatically run in series.
This is my current code:
beforeAll(function(done) {
Fixtures.clearDB(done);
});
beforeAll(function(done) {
Fixtures.createLecture({}, function(error, result) {
lectureCode = result;
done();
});
});
beforeAll(function(done) {
Fixtures.createQuestion({}, done);
});
beforeAll(function(done) {
Router.go('lecturePage', {lectureCode: lectureCode});
Tracker.afterFlush(done);
});
beforeAll(waitForRouter);
it("....", function() {
...
});
How can I write this code in Jasmine in a pretty style without going into callback hell?
The Source Code of the entire application is open source and can be found on GitHub
Thank you very much in advance,
Max
Here you are:
beforeAll(function(done) {
async.series([
function(callback) {
Fixtures.clearDB(callback)
},
function(callback) {
Fixtures.createLecture({}, function(error, result) {
lectureCode = result;
callback();
});
},
function(callback) {
Fixtures.createQuestion({}, callback);
},
function(callback) {
Router.go('lecturePage', {lectureCode: lectureCode});
Tracker.afterFlush(callback);
}],function(err, results){ // callback called when all tasks are done
done();
});
}
I haven't tested it but I hope you get an idea. You need to create list of functions, each of them will be provided with callback function that you need to call to make next function run. After that final callback is called where we can call done() to tell jasmine that we're done. Hope this helps.
My general approach would be to have a single beforeAll block.
Inside that block, use a promise API to chain all these calls as promises.
beforeAll(function(done) {
Fixtures.clearDB(done).then(...
});
beforeAll(waitForRouter);
it("....", function() {
...
});
I'm very new to unit tests, mocha, and should.js, and I'm trying to write a test for an asynchronous method that returns a promise. Here is my test code:
var should = require("should"),
tideRetriever = require("../tide-retriever"),
moment = require("moment"),
timeFormat = "YYYY-MM-DD-HH:mm:ss",
from = moment("2013-03-06T00:00:00", timeFormat),
to = moment("2013-03-12T23:59:00", timeFormat),
expectedCount = 300;
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function() {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
},
function(err) { // reject
should.fail("Promise rejected", err);
}
);
});
});
When I manually test the tideRetriever.get method, it consistently resolves an array of 27 elements (as expected), but the test will not fail regardless of the value of expectedCount. Here is my simple manual test:
tideRetriever.get(from, to).then(
function(entries) {
console.log(entries, entries.length);
},
function(err) {
console.log("Promise rejected", err);
}
);
I can also post the source for the module being tested if it's necessary.
Am I misunderstanding something about Mocha or should.js? Any help would be greatly appreciated.
UPDATE
At some point Mocha started to support returning Promise from test instead of adding done() callbacks. Original answer still works, but test looks much cleaner with this approach:
it("should retrieve and parse tide CSV data", function() {
return tideRetriever.get(from, to).then(
function(entries) {
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
}
);
});
Check out this gist for complete example.
ORIGINAL
CAUTION. Accepted answer works only with normal asynchronous code, not with Promises (which author uses).
Difference is that exceptions thrown from Promise callbacks can't be caught by application (in our case Mocha) and therefore test will fail by timeout and not by an actual assertion. The assertion can be logged or not depending on Promise implementation. See more information about this at when documentation.
To properly handle this with Promises you should pass err object to the done() callback instead of throwing it. You can do it by using Promise.catch() method (not in onRejection() callback of Promise.then(), because it doesn't catch exceptions from onFulfilment() callback of the same method). See example below:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done(); // test passes
},
function(err) { // reject
done(err); // Promise rejected
}
).catch(function (err) {
done(err); // should throwed assertion
});
});
});
PS done() callback is used in three places to cover all possible cases. However onRejection() callback can be completely removed if you don't need any special logic inside it. Promise.catch() will handle rejections also in this case.
When testing asynchronous code, you need to tell Mocha when the test is complete (regardless of whether it passed or failed). This is done by specifying an argument to the test function, which Mocha populates with a done function. So your code might look like this:
describe("tide retriever", function() {
it("should retrieve and parse tide CSV data", function(done) {
tideRetriever.get(from, to).then(
function(entries) { // resolve
entries.should.be.instanceof(Array).and.have.lengthOf(expectedCount);
done();
},
function(err) { // reject
should.fail("Promise rejected", err);
done();
}
);
});
});
Note that the way Mocha knows this is an async test and it needs to wait until done() is called is just by specifying that argument.
Also, if your promise has a "completed" handler, which fires both on success and failure, you can alternatively call done() in that, thus saving a call.
More info at:
http://mochajs.github.io/mocha/#asynchronous-code