Read on to learn more about the testing helpers Yeoman add to ease the pain of unit testing a generator.
The examples below assume you use Mocha in BDD mode. The global concept should apply easily to your unit testing framework of choice.
Organizing your tests
It is important to keep your tests simple and easily editable.
Usually the best way to organize your tests is to separate each generator and sub-generator into its own describe
block. Then, add a describe
block for each option your generator accept. And then, use an it
block for each assertion (or related assertion).
The code running the generator should be located in a before
or beforeEach
block - it usually doesn't belong with your assertions.
In code, you should end up with a structure similar to this:
describe('backbone:app', function () {
describe('when using require.js', function () {
before(function () {
// Mock the options, set up an output folder and run the generator
});
it('generate a router.js file', function () {
// assert the file exist
// assert the file uses AMD definition
});
it('generate a view file');
it('generate a base controller');
});
});
Test helpers
Yeoman provide test helpers methods. They're contained inside the yeoman-test
package.
var helpers = require('yeoman-test');
You can check the full helpers API here. These methods will usually be run inside a before
block.
The most useful method when unit testing a generator is helpers.run()
. This method will return a RunContext instance on which you can call method to setup a directory, mock prompt, mock arguments, etc.
var path = require('path');
before(function () {
return helpers.run(path.join( __dirname, '../app'))
.withOptions({ foo: 'bar' }) // Mock options passed in
.withArguments(['name-x']) // Mock the arguments
.withPrompts({ coffee: false }) // Mock the prompt answers
.toPromise(); // Get a Promise back for when the generator finishes
})
Sometimes you may want to construct a test scenario for the generator to run with existing contents in the target directory. In which case, you could invoke inTmpDir()
with a callback function, like so:
var path = require('path');
var fs = require('fs-extra');
helpers.run(path.join( __dirname, '../app'))
.inTmpDir(function (dir) {
// `dir` is the path to the new temporary directory
fs.copySync(path.join(__dirname, '../templates/common'), dir)
})
.withPrompts({ coffee: false })
.toPromise();
You can also perform asynchronous task in your callback:
var path = require('path');
var fs = require('fs-extra');
helpers.run(path.join( __dirname, '../app'))
.inTmpDir(function (dir) {
var done = this.async(); // `this` is the RunContext object.
fs.copy(path.join(__dirname, '../templates/common'), dir, done);
})
.withPrompts({ coffee: false })
.toPromise();
When using .toPromise()
, the returned Promise will resolve with the directory that the generator was run in. This can be useful if you want to use a temporary directory that the generator was run in:
helpers.run(path.join( __dirname, '../app'))
.inTmpDir(function (dir) {
var done = this.async(); // `this` is the RunContext object.
fs.copy(path.join(__dirname, '../templates/common'), dir, done);
})
.withPrompts({ coffee: false })
.toPromise()
.then(function (dir) {
// assert something about the stuff in `dir`
});
If your generator calls composeWith()
, you may want to mock those dependent generators. Using #withGenerators()
, pass in array of arrays that use #createDummyGenerator()
as the first item and a namespace for the mocked generator as a second item:
var deps = [
[helpers.createDummyGenerator(), 'karma:app']
];
return helpers.run(path.join( __dirname, '../app'))
.withGenerators(deps)
.toPromise();
If you hate promises, you can use the 'ready'
, 'error'
, and 'end'
Events emitted:
helpers.run(path.join( __dirname, '../app'))
.on('error', function (error) {
console.log('Oh Noes!', error);
})
.on('ready', function (generator) {
// This is called right before `generator.run()` is called
})
.on('end', done);
Assertions helpers
Yeoman extends the native assert module with generator related assertions helpers. You can see the full list of assertions helpers on the yeoman-assert
repository.
Require the assertion helpers:
var assert = require('yeoman-assert');
Assert files exists
assert.file(['Gruntfile.js', 'app/router.js', 'app/views/main.js']);
assert.noFile()
assert the contrary.
Assert a file content
assert.fileContent('controllers/user.js', /App\.UserController = Ember\.ObjectController\.extend/);
assert.noFileContent()
assert the contrary.