I found out testdouble
can be used after-all by using a require
statement instead of an import
. I did not realize at first, because in my understanding babel would have compiled import
statements to require
statements behind the scenes. And although this is true, it appeared that babel also moves the import statements to the top of the file.
Thereby; the import
statements are being placed above the td.replace
statements; and thereby nothing is being replaced after all.
You can easily verify this by pasting the code below in the babel repl, and see that:
td.replace('meteor/meteor', { Meteor });
import{ post } from '../posts.js';
const { savePost } = require('../post.js');
will be transpiled to:
var _posts = require('../posts.js');
td.replace('meteor/meteor', { Meteor: Meteor });
var _require = require('../post.js');
var savePost = _require.savePost;
So translating the opening post’s example from proxyquire
+ sinon
to testdouble
, makes the following test-case:
// tests/post.js - first part
/* eslint-env mocha */
import { expect } from 'chai';
import td from 'testdouble';
const Meteor = td.object(['userId']);
const FlowRouter = td.object(['go']);
td.replace('meteor/meteor', { Meteor });
td.replace('meteor/kadira:flowrouter', { FlowRouter });
const { post, savePost } = require('../post.js');
Note that we use require
for the import of ../posts.js
to make the replacements work. At first I would have preferred to keep using imports. But on the other hand, now there is a visual indicator of importing files as they are, and importing files with applied replacements.
And the testing itself:
// tests/post.js - second part
describe('posts', () => {
before(() => {
td.when(Meteor.userId()).thenReturn('user_1');
});
after(() => {
td.reset();
});
it('now stubs Meteor packages', () => {
const result = post(
{ title: 'foo', body: 'bar' },
{ type: 'SAVE_POST' }
);
expect(result).to.have.property('userId', 'user_1');
});
it('can spy on Meteor packages', () => {
const result = savePost(
{ title: 'foo', body: 'bar' },
{ type: 'SAVE_POST' }
);
td.verify(FlowRouter.go('/home'));
});
});
You’ll see that these tests get an after
method to undo the rewires after the tests have been run. Do not forget this! Mocha will forgive you, but Wallaby will not, as wallaby reuses the node processes.
As wallaby reuses node processes, and can stop a running test script half way the cycle to start it again, because you for example keep typing, it is advised to add the testdouble.reset
in your wallaby.setup
function. In this way we are sure that no modules are stubbed out before our testfiles are being reloaded. This is in addition to the after
method! It’s not an replacement.
// wallaby.js
setup: () => {
require('testdouble').reset();
},