evantahler/actionHero — pull request #293 by evantahler (2014-01-05 05:37:32+00:00)
Title: Test suite

## SpecHelper

This PR formally adds a `specHelper` to actionHero to aid in headless testing. We do this by including a new `specHelper` initializer which build up a new server, `testServer` in the test environment. Via the `testServer`, you can easily call actions or tasks without making a real request.

We have chosen [mocha](http://visionmedia.github.io/mocha/) as our test framwork and [should](https://github.com/visionmedia/should.js/) as our assertion tool which we will generate as dependancies for all new projects. You do not need to use these testing tools, but an example will be provided which makes use of them.

This PR also refactors many of actionHero's internal tests to use the new `testServer` rather than making HTTP requests. Of course, the specific tests for each of the servers still make full 'integration' connections.

### Test Methods

#### `new api.specHelper.connection()`
- generate a new connection object for the `testServer`
- this connection can run actions, chat, etc.
- `connection.messages` will contain all messages the connection has been sent (welcome messages, action responses, say messages, etc)

#### `api.specHelper.runAction(actionName, input, callback)`
- use this method to run an action
- `input` can be either a `api.specHelper.connection` object, or simply a hash of params, IE: `{key: 'value'}`
- the callback returns `message` and `connection`.
- example use:

```javascript
api.specHelper.runAction('cacheTest', {key: 'key', value: 'value'}, function(message, connection){
// message is the normal API response;
// connection is a new connection object
})
```

#### `api.specHelper.getStaticFile(file, callback)`
- request a file in `/public` from the server
- the callback returns `message` and `connection` where `message` is a hash:

```javascript
var message = {
error : error, // null if everything is OK
content : (string), // string representation of the file's body
mime : mime, // file mime
length : length // bytes
}
```

#### `api.specHelper.runTask(taskName, params, callback)`
- callback may or may not return anything depending on your task's makeup

## Suggested Test Layout

We can now suggest a 'best practice' to testing actionHero. Here is an example for testing the `randomNumber` action:

```javascript
var should = require('should');
var actionHeroPrototype = require(__dirname + "/../../actionHero.js").actionHeroPrototype;
var actionHero = new actionHeroPrototype();
var api;

describe('Action: Random Number', function(){

before(function(done){
actionHero.start(function(err, a){
api = a;
done();
})
});

after(function(done){
actionHero.stop(function(err){
done();
});
});

var firstNumber = null;
it('generates random numbers', function(done){
api.specHelper.runAction('randomNumber', function(response, connection){
response.randomNumber.should.exit
response.randomNumber.should.be.a.Number;
response.randomNumber.should.be.within(0,1);
firstNumber = response.randomNumber;
done();
});
});

it('is unique / random', function(done){
api.specHelper.runAction('randomNumber', function(response, connection){
response.randomNumber.should.be.a.Number;
response.randomNumber.should.not.equal(firstNumber);
done();
});
});

});
```

## Misc

- removed grunt+mocha helpers to return to a cleaner `npm test` using only mocha. This makes the test framework easier to understand
- in the `actionProcessor` and `cache`, we have changed from `process.nextTick` to `setImmediate` in an attempt to throttle incoming requests under heavy load
- misc cleanup