Skip to content

Instantly share code, notes, and snippets.

@sungwoncho
Last active January 5, 2016 10:49
Show Gist options
  • Save sungwoncho/172fc39f4ac2bdc93dc1 to your computer and use it in GitHub Desktop.
Save sungwoncho/172fc39f4ac2bdc93dc1 to your computer and use it in GitHub Desktop.
Coddee test example

Coddee test example

An example code from coddee. These are a set of tests written using mocha and chai.

In webhook_method_tests.js, I am testing that the method saves a MongoDB document into a collection.

In webhook_tests.js, I am testing that this method is called when a webhook comes in from GitHub.

I wrote two different tests because the middleware that detects the GitHub webhook is an event emitter, and I don't have much control over how long it takes to actually save a document to a collection. The fact that I am testing an asynchronous middleware means that I have to use setTimeout to make my assertion. Also, write operations are not always cheap, and I have to delay my assertion for quite a bit (~800ms), which is unacceptable if you have a large test suite. In spite of the significantly delayed assertion, the test would be still brittle because sometimes DB operation might take longer than my delay.

I addressed the problem of delayed assertion by separating the tests: one for testing that the correct method is called, and another for testing that the method saves the data in the db. In the first one, I still need to use setTimeout, but the delay is only 50ms, and the test is very solid.

MochaWeb.testOnly(function(){
var sinon = Meteor.npmRequire('sinon');
// This is how you require an npm package in Meteor
describe("webhook methods", function(){
beforeEach(function() {
sinon.stub(Coddee.parser, 'getDiff');
sinon.stub(Coddee.parser, 'persistDiff');
});
afterEach(function() {
Repos.remove({});
PullRequests.remove({});
Revisions.remove({});
Coddee.parser.getDiff.restore();
Coddee.parser.persistDiff.restore();
});
describe("opened", function(){
it("creates a pullRequest document and a revision", function(){
var payload = JSON.parse(
Assets.getText('fixtures/github_webhook/pull_request/opened.txt')
);
FactoryBoy.create('userRepo', {repoId: payload.pull_request.base.repo.id});
// FactoryBoy is an open source project I initiated.
Coddee.webhook.pr.opened(payload);
chai.expect(PullRequests.find().count()).to.equal(1);
chai.expect(Revisions.find().count()).to.equal(1);
});
});
// ...
});
});
var generateSignature = function (payload) {
var crypto = Npm.require('crypto');
var hmac = crypto.createHmac('sha1', Meteor.settings.GitHubWebhookSecret); // Meteor.settings is how you can safely access secret variables without committing them to the version control in Meteor
hmac.update(JSON.stringify(payload));
return 'sha1=' + hmac.digest('hex');
};
MochaWeb.testOnly(function(){
var sinon = Meteor.npmRequire('sinon');
describe("pull_request webhook", function(){
afterEach(function() {
Repos.remove({});
PullRequests.remove({});
Revisions.remove({});
});
describe("opened", function(){
it("calls Coddee.webhook.pr.opened with payload", function(done){
var mock = sinon.mock(Coddee.webhook.pr);
mock.expects('opened');
var endpointUrl = Meteor.absoluteUrl('webhook');
var payload = JSON.parse(
Assets.getText('fixtures/github_webhook/pull_request/opened.txt') // Get a test fixture
);
var headers = {
'Request URL': endpointUrl,
'Request method': 'POST',
'content-type': 'application/json',
'Expect': '',
'User-Agent': 'GitHub-Hookshot/4963429',
'X-GitHub-Delivery': 'c9441380-4609-11e5-9099-2600e79e587b',
'X-GitHub-Event': 'pull_request',
'X-Hub-Signature': generateSignature(payload)
};
FactoryBoy.create('userRepo', {repoId: payload.pull_request.base.repo.id});
HTTP.post(endpointUrl, {
headers: headers,
data: payload
});
// Meteor.bindEnvironment is needed because almost everything in Meteor runs on Fiber
// and we need to run the callback on the proper Fiber to avoid unexpected behaviors.
Meteor.setTimeout(Meteor.bindEnvironment(function () {
mock.verify();
done();
}), 50);
});
});
//...
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment