I have some ES5 example project that I would like to convert to ES6:
https://github.com/stefaneidelloth/testDemoES5
https://github.com/stefaneidelloth/testDemoES6
The examples include a class Qux
which inherits from a class Baa
.
When testing Qux
, I would like to mock Baa
.
For ES5 I use Squire.js to mock AMD module dependencies and the unit tests work just fine.
Unfortunately I could not find a testing framework that supports ES6 (="ECMAScript 2015 Language", ES2015) modules directly. We now have 2020 and there are still no unit tests for ES2015? I already spend a lot of time trying to get these tests working ... and I have the impression that my approach is missing something.
Since I could not find direct support for ES6 tests, I try to stick to karma and use webpack to translate the ES6 module code to ES5 AMD modules for testing.
Lets first consider to use karma in combination with requirejs and ES6 code that has been translated to AMD modules.
A. If I try to mock a translated class Baa
(module 'src/baa') with Squire ... that does not work any more. Webpack puts all dependencies in a single file and when using 'src/qux', my injected 'src/baa' is not considered.
qux.test.js:
define([
'squire'
], function (
Squire
) {
describe('Qux', function(){
var sut;
beforeEach(function(done){
var injector = new Squire();
injector.mock('src/baa', createBaaMock());
injector.require([
'src/qux'
], function(
quxModule
){
var Qux = quxModule.default;
sut = new Qux('qux');
done();
});
});
it('quxMethod', function(){
expect(sut.quxMethod()).toEqual('quxMethod');
});
it('baaMethod', function(){
expect(sut.baaMethod()).toEqual('baaMockedMethod');
});
it('overridableMethod', function(){
expect(sut.overridableMethod()).toEqual('qux');
});
function createBaaMock(){
var BaaMock = function (name) {
this.name = name;
};
BaaMock.prototype.baaMethod = function () {
return 'baaMockedMethod';
}
var moduleMock = {
default: BaaMock
}
return moduleMock;
}
});
});
=> I get the error
Expected 'baaMethod' to equal 'baaMockedMethod'.
Some furhter info on debugging.... The translation to ES5 has the disadvantage that when debugging tests, the code that runs in the browser looks different than the original code (by default). Therefore, possible bugs are harder to identify. What helps here is to:
B. I tried to use inject-loader, an alternative to Squire.js, which knows about webpack:
https://github.com/LeanKit-Labs/inject-loader
However, that seems to be a CommonJs module which is not compatible to my karma + requirejs project:
qux.test.js:
describe('Qux', function(){
var sut;
beforeEach(function(done){
require(['inject!src/qux'],function(ModuleInjector){
var quxModule = ModuleInjector({
'src/baa': crateBaaMock()
});
var Qux = quxModule.default;
sut = new Qux('qux');
done();
});
});
it('quxMethod', function(){
expect(sut.quxMethod()).toEqual('quxMethod');
});
it('baaMethod', function(){
expect(sut.baaMethod()).toEqual('baaMockedMethod');
});
it('overridableMethod', function(){
expect(sut.overridableMethod()).toEqual('qux');
});
function createBaaMock(){
var BaaMock = function (name) {
this.name = name;
};
BaaMock.prototype.baaMethod = function () {
return 'baaMockedMethod';
}
var moduleMock = {
default: BaaMock
}
return moduleMock;
}
});
=> I get the error
Uncaught ReferenceError: module is not defined.
I also tried mock-loader but did not get it working.
C. I tried to not use AMD modules but CommonJs modules as target for the webpack compilation. However, I did not manage to use the commonjs proprocessor and the and webpack preprocesser of karma together.
D. I tried to use system.js instead of require.js and webpack with Karma. However, karma-system.js relies on a very old version of system.js (0.19.47) and I did not get it working.
E. In an answer to a related and old SO question someone suggests to use "import * as obj" style to import a class in form of a module and then spy on the default export to mock the class.
However, that might cause issues if several tests are using that "modified module" (the property "default" can not be redefined).
Since webpack does not dynamically load dependencies, following test fails:
define([
'src/baa',
'src/qux'
],function(
baaModule,
quxModule
){
describe('Qux', function(){
var sut;
beforeEach(function(done){
baaModule.default = createBaaMock();
var Qux = quxModule.default;
sut = new Qux('qux');
done();
});
it('quxMethod', function(){
expect(sut.quxMethod()).toEqual('quxMethod');
});
it('baaMethod', function(){
expect(sut.baaMethod()).toEqual('baaMockedMethod');
});
it('overridableMethod', function(){
expect(sut.overridableMethod()).toEqual('qux');
});
function createBaaMock(){
var BaaMock = function (name) {
this.name = name;
};
BaaMock.prototype.baaMethod = function () {
return 'baaMockedMethod';
}
var moduleMock = {
default: BaaMock
}
return moduleMock;
}
});
});
In summary, I found a lot of outdated and incomplete approaches for testing ES6 modules and none of them seems to work out just nicely.
=> If I should stay with karma, how do I need to adapt my test qux.test.js example code (and probably my configuration files) to correctly mock the class Baa
?
=> Is it possible to tell webpack to keep the translated modules separate, so that I can easily inject the dependencies with Squire.js?
=> Is there a better and up to date work flow/framework for unit testing ES6 modules in the browser? Did someone try to combine jest with karma?
Related stuff:
See Question&Answers more detail:
os