Test files are written in JavaScript, and should be placed inside the folder defined by the directory
CLI flag or .grandmarc
file. All JavaScript files in the folder and all subfolders will be considered test files.
A test will export a single object, consisting of the following methods (in order or execution):
beforeAll
- runs first, before any tests are started. This function will run only once.beforeEach
- runs before each execution of a test. It will run for the same amount of iterations as the test. It is not reflected in the time reported for the test.test
- the actual code being tested, and is the only require function in the test file. This is the only function that accounts for the time when generating reports. The test function context also has special methods that allow for reporting custom metrics.afterEach
- runs after each execution of a test. It will run for the same amount of iterations as the test. It will always run, even if the test is failed. It is not reflected in the time reported for the test.afterAll
- runs once, after all tests have completed.
Note that all methods other than test
are optional and can be left out.
All methods will be called with a single parameter -- done
-- which is a function to call when that method is complete.
beforeEach
, test
, and afterEach
will all run in a worker thread, and are considered a full test run (as defined by the rate
or concurrent
parameter). However, only the test
function is timed and used for statistics.
This is an example of a full test file:
module.exports = {
beforeAll: function(done) {
process.nextTick(done);
},
beforeEach: function(done) {
process.nextTick(done);
},
test: function(done) {
process.nextTick(done);
},
afterEach: function(done) {
process.nextTick(done);
},
afterAll: function(done) {
process.nextTick(done);
}
};
Each of the functions above will be called with a context object -- the this
keyword of the function. This can be used to share values between all of the functions. Since grandma
can run the code in different threads, this is the only safe way to share state.
beforeAll
and afterAll
will always execute in the parent thread, and not the same thread as any of the individual tests. Though they will get a context object defined, some JavaScript values cannot be shared across threads (not to mention, changes in one thread will not be reflected in another thread). Therefore, only strings and numbers set in the beforeAll
method will be passed into thread workers to be used for the remaining functions. Also note that this means modifying values on the context object will not guarantee that any further tests will get those same values. The same goes for attempting to store state in objects in the test files, as there is no guarantee that they will execute in the same thread.
It is possible to run the tests using a single worker thread (see the thread
parameter in the API and CLI of grandma
), in which case, state objects created during beforeEach
will be available to test
and afterEach
. However, beforeAll
and afterAll
will still execute in the parent thread and cannot share state other than strings and numbers.
If you have a valid use case that is limited by the above, I would love to hear it. Please submit a new issue.
module.exports = {
beforeAll: function(done) {
var auth = getAuthValues();
this.authUser = auth.user;
this.authPassword = auth.password;
process.nextTick(done);
},
beforeEach: function(done) {
var uniqueId = getUniqueTestId();
this.id = uniqueId;
process.nextTick(done);
},
test: function(done) {
var result = doSomeWork(this.authUser, this.authPassword);
doSomeAsyncWork(result, this.id, done);
},
afterEach: function(done) {
cleanUpAfterWork(this.id);
process.nextTick(done);
}
};
In the test
function, you can pass an error (or any truthy value) to the done
callback to fail the test. Further, if the value you pass in has an errorCode
property, that property will be used in order to group different failures together.
Note: if you do not provide an error code, 0
will be used when binning and reporting errors. If a test times out, it will use an error code of -1
. It is best to avoid using 0
or -1
as the provided error code, in order to avoid confusion.
module.exports = {
test: function(done) {
if (someTest) {
// successfully complete the test
done();
} else if (someError) {
// fail the test with specific errorCode
done({ errorCode: 'someError' });
} else {
// fail the test for an unknown reason
done(new Error('stuff happened'));
}
}
};