/ #Javascript #testing 

Integration Testing a Restful Endpoint with Request and Jasmine and Node.JS

What is it for?

I am building a simple restful API using Express over MongoDB in order to support a mobile application.  Even though the endpoint was quite straightforward, I wanted to ensure it is properly tested.  In creating this test, I wanted to fulfill some simple objectives

  • Integration style test - completely independent of the implementation
  • Setup and Teardown of the database.
  • Fast, simple, as little complexity as possible

What Am I Testing?

The API is simple ../rules/clientId/ruleId (get) - Retrieve a specific rule for a specific client ../rules/clientId (get) - Retrieve all of the rules for a specific client ../rules (post) - Store a rule ../rules/clientId/ruleId (delete) - Delete a specific rule for a specific client

Why Use Jasmine? Why not use Frisby.js?

Ah tool choice, such a fraught area.  Jasmine is well established and  there is a great node port of Jasmine here that is reasonably active.  All you Mocha folks and Karma fanatics, pls comment and tell me why framework X is better than Jasmine :)

Frisby.js seems to have a lot of search engine traction but It doesn’t support setup/teardown () and seemed to force me into a certain way of doing things which I didn’t like.

Enough already let’s do it

The full gist is here if you want to look at it…

Setup

To setup, I use beforeEach with the done option because I want to asynchronously move on to the test once the MongoDB call is finished.  To setup for the tests, I insert a single ‘rule’ document containing just ID information.

 

beforeEach(function(done){ 

MongoClient.connect("mongodb://" + config.mongo_db_address + ":" + config.mongo_db_port + "/" + config.mongo_db_name, function (err, db) { 

var collection = db.collection('rules'); 

collection.insert({"clientId": "test","ruleId": "test1"}, function (err, result) { 

db.close(); 

done(); 

}); 

}); 

}); 

Teardown

To teardown, I issue a global delete for all rules belonging to the test client.  This is a bit crude but as in all integration style testing, I don’t have the luxury of simply making tests transactional.  Note that again, I am using the Async form and using a done method to indicate that things can move on.

 afterEach(function(done) { 

MongoClient.connect("mongodb://" + config.mongo_db_address + ":" + config.mongo_db_port + "/" + config.mongo_db_name, function (err, db) { 

var collection = db.collection('rules'); 

collection.remove({ "clientId": "test" }, function (err, result) { 

db.close(); 

done(); 

}); 

}); 

}); 

Update: I am now using uri: not url: and also using the json:true parameter in the request options.  this means I don’t need to JSON.parse the bodyString like I was before.. much cleaner.

Testing the Get

Testing the getter method is simple.  I simply use the request component to retrieve the body string from the endpoint, use JSON.parse to convert it to an object littoral and then assert its values.

 it("should retrieve a specific rule", function(done) { 

request({ 

uri: "http://localhost:3000/api/rules/test/test1", 

json: true 

}, function(error, response, body){ 

expect(body.clientId).toEqual("test"); 

expect(body.ruleId).toEqual("test1"); 

done(); 

}); 

}); 

Testing the Post

For the Post, I really should check the addition to the DB but I cheat a bit by making a nested get call and verifying the return result (I use the same Kluge for delete).

 

it("should store a rule", function(done) { 

request({ 

method:'POST', 

uri: "http://localhost:3000/api/rules", 

json:{"clientId": "test","ruleId": "test2"} 

}, function(error, response, bodyPost){ 

request({ 

url: "http://localhost:3000/api/rules/test/test2", 

json:true 

}, function(error, response, body){ 

expect(body.ruleId).toEqual("test2"); 

done(); 

}); 

}); 

}); 

Thats it, hope this helps.  I certainly thought this would be easier than it was and had dead ends with Frisby.js (no setup/teardown) and fiddling about with Request but this works.  All comments and suggestions accepted.

Michael

https://gist.github.com/mdausmann/759ad37fe4f9e77244da

Author

Michael Dausmann

Full stack developer. Agile, Data science and Machine Learning. Love learning, speaking and dad jokes. Tweets are my own.