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:
it()
).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('[email protected]');
});
});
});
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:
forEach
for example)Some facts before continue:
describe()
in file is completed it starts execution of collected tests.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:
describe()
creates namespace 1
and synchronously executes cb1()
.it()
synchronously adds test 1a
.it()
synchronously adds test 1b
.cb1()
is completed, as well as test collection step.Addition of IIFE doesn't change anything, since it's just calls itself synchronously.
Let's trace your second case:
describe()
creates namespace 1
and synchronously executes cb1()
.cb1()
is completed, as well as test collection step.it()
tries to add test 1a
, but execution of tests is already completed.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).