TestinginNode. jswithMocha

Source: Internet
Author: User
Tags install node node server
IrecentlymigratedallofourserversideNodetestsatInstinctfromVowstoMocha. I & amp; #39; vebeenaroundtheblockwithtestinginNode, unabletofindanapproachth...

I recently migrated all of our server side Node tests at Instinct from Vows to Mocha.

I 've been around the block with testing in Node, unable to find an approach that I really liked, until I started using Mocha. the elegant way it does async along with familiar BDD style syntax is really enjoyable to work. it's made writing/maintaining our tests something I don't run from anymore.

Two main reasons I made the switch:

Syntax: The bulk of our code for Instinct is on the client-side where we were usingJasmine. personally, I prefer the BDD style of Jasmine with nested closures over Vows 'syntax. I knew getting all our Javascript tests consistently using the same syntax wocould be a big win and keep me dedicated to writing and maintaining our tests.

Better Async Testing on the Client + Server: The way Mocha does async testing is very elegant. I was becoming frustrated with Jasmine's approach to async testing, which is basically to force it to run synchronously with timers. I wanted to first try Mocha on the server, and then if it went well I knew I cocould somewhat easily migrate my client-side tests to Mocha as well.

Plus we had to do a big re-factor/rewrite of several pieces on the server anyway, so it seemed like a good time to make the switch.

First Steps
First you need to install Mocha. I justed added it to our package. json and installed it locally under our project. Or you can just install it manually:

Npm install mocha
We were using Vows for both unit and integration tests. So after installing things, I wanted to first get our unit tests working. This was literally just a matter of updating the syntax of our tests.

From something like this in Vows:

Vows. describe ('user model'). addBatch ({

'Create a new user ':{

Topic: function (){
User. create ({username: 'test'}, this. callback );
},

'Could have a username': function (doc ){
Assert. equal ('test', doc. username );
}
}
}). Export (module );
To this in Mocha:

Describe ('Creating a new user', function (){
Var user;

Before (function (done ){
User. create ({username: 'test', function (err, u ){
User = u;
Done ();
});
});

It ('could have a username', function (){
User. shocould. have. property ('username', 'test ');
});
});
I opted to use the 'shot' syntax, which is a separate module you need to install & require in your tests.

Running the tests
To run the tests, I followed TJ's example of setting up a MakeFile in the root of the project, like this:

REPORTER = dot

Test:
@ NODE_ENV = test./node_modules/. bin/mocha \
-- Reporter $ (REPORTER )\

Test-w:
@ NODE_ENV = test./node_modules/. bin/mocha \
-- Reporter $ (REPORTER )\
-- Growl \
-- Watch

. PHONY: test-w
Then you can just run your tests:

$ Make test
By default mocha will run everything in/tests off of your main project. I 've been following the same organization for my test files as TJ uses in Express, where all the tests are in that main/tests directory and the filenames indicate the namespacing.

I opted for the 'dot 'reporting output, but there's tons of options. and it's even pretty easy to fork and create your own if you're looking for something specific in your test output.

Auto-running the tests

Just like autotest in Rails, you can setup Mocha to run automatically whenever one of your files changes. that was the purpose of the test-w target in the MakeFile above. so now I cocould just make my tests run continuously in the background by running:

Make test-w
To make it work on Lion with growl notifications, I needed to install growlnotify and thennode-growl.

Setting up 'npm Test'
The next thing I did was modify our package. json file so that it runs our tests whenever someone executes 'npm test' in the project.

Everyone in the Node community seems to have their own methods for running tests, but one thing that does seem like it's standardizing is that the command 'npm test' will run the tests for a node project. you can do this just by adding it under the 'scripts' attribute in your package. json file:

{
"Name": "instinct ",
"Version": "0.0.1 ",
"Dependencies ":{},
"Scripts ":{
"Test": "make test"
}
}
Now you can also run your tests via npm:

Npm test
Migrating our Integration Tests

My next step was getting the integration tests running in Mocha. our node server is essential just a json rest api, so our integration tests are largely HTTP requests that test response codes + the json of the response body.

Vows didn't have any built-in support for HTTP testing, so I created my own helper methods to do this. I was a little worried I 'd have to rewrite these for Mocha as it doesn't seem to have anything built-in to help with Integration tests.

After poking through the tests for Express however, I found that TJ had a support lib that he was using to test HTTP requests/responses. I was able to create a modified versionthat gave me everything I had in Vows.

And then I was able to write nice looking HTTP tests like this:

Var app = require ('../app ')
, Http = require ('support/http ');

Describe ('user API ', function (){

Before (function (done ){
Http. createServer (app, done );
});

It ('get/users shocould return 200 ', function (done ){
Request ()
. Get ('/users ')
. Secondary CT (200, done );
});

It ('Post/users shocould return 200 ', function (done ){
Request ()
. Post ('/users ')
. Set ('content-type', 'application/json ')
. Write (JSON. stringify ({username: 'test', password: 'pass '}))
. Secondary CT (200, done );
});
});
'App' is your main Express app and 'http' is the support lib.

Generating a Coverage Report
The last piece of the puzzle was getting a test coverage report. this wasn't something I had setup with Vows, but now seemed like a good time to do it as Mocha's coverage reports looked pretty sweet.

The first thing I did was install node-jscoverage.

To generate a coverage report you need to first instrument your code so that when it runs it can track what lines have been called and which haven't. this is what node-jscoverage does, it will be essential to create a copy of every file you tell it. you then need to make your tests use the instrumented code instead of the normal code.

This is when I realized I had a problem. my source code was primarily in 3 different directories off the project root, 'controllers', 'models' and 'middleware '. I wowould need to instrument all 3 directories, and then get my tests to be smart about whether to use the instrumented. non-instrumented code.

I didn't see a very clean way to do this, so I started poking around how Express and other projects handled this. I found that it becomes very simple if you organize all of your source code under a single directory (I. e. lib) and export it all as a single module. then you can create an index. js file (just like Express) at the root of the project that simply exports either/lib or/lib-cov based on the some environment variable.

Our project was laid out like this:

Config/
Controllers/
Middleware/
Models/
Public/
Routes/
Test/
Views/
App. js
Package. json
MakeFile
After rearranging things, it now looks somewhat like this:

Public/
Lib/
Config/
Controllers/
Middleware/
Models/
Instinct. js
Routes/
Test/
Views/
App. js
Packageon. json
MakeFile
Index. js
I had to add instinct. js that uses lidated all my models, controllers, middleware and exported them from one place. then elsewhere in my project anytime I needed a model, controller or middleware I just required that main instinct module like so:

Var instinct = require ('./instinct ');
Then I added an index. js file at the project root that exports either lib/instinct or lib-cov/instinct depending on the INSTINCT_COV environment variable (which we'll set in our MakeFile ):

Module. exports = process. env. INSTINCT_COV
? Require ('./lib-cov/instinct ')
: Require ('./lib/instinct ')
Now when node-jscoverage instruments my lib/code to lib-cov/, if my environment variable INSTINCT_COV is set to true, any require to './instinct' will load the instrumented code.

Now with my project organized, I just had to modify my MakeFile, again borrowing from the MakeFile in Express:

REPORTER = dot

Test:
@ NODE_ENV = test./node_modules/. bin/mocha \
-- Reporter $ (REPORTER )\

Test-w:
@ NODE_ENV = test./node_modules/. bin/mocha \
-- Reporter $ (REPORTER )\
-- Growl \
-- Watch

Test-cov: lib-cov
@ INSTINCT_COV = 1 $ (MAKE) test REPORTER = html-cov> public/coverage.html

Lib-cov:
@ Jscoverage lib-cov

. PHONY: test-w
And that was it, I was able to generate a coverage report by running:

Make test-cov
Next Steps
After running with Mocha on the server for a bit, I was really impressed with how nice it made testing, and confirmed that I definitely wanted to migrate our client-side tests too (which I have since completed & plan to write about in another post ).

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.