Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.9k views
in Technique[技术] by (71.8m points)

debugging - How to mock dependencies for ES6 unit tests?

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I switched from karma to jest and here is a working demo project:

https://github.com/stefaneidelloth/testDemoES6Jest

The workflow is still based on a transpiler (babel) but that happens in the background and does not really influence the development experience.


Example test code that mocks some ES6 module:

import Qux from './../src/qux.js';

jest.mock('./../src/baa.js', () => {
    return class BaaMock {
        constructor(name){
            this.name = name;
        }

        baaMethod(){
            return 'baaMockedMethod';
        }
    }   
});

describe('Qux', function(){

    var sut;        

    beforeEach(function(){                  
        sut = new Qux('qux');
    });

    it('quxMethod', function(){         
        expect(sut.quxMethod()).toEqual('quxMethod');
    }); 

    it('baaMethod', function(){         
        expect(sut.baaMethod()).toEqual('baaMockedMethod');
    }); 

    it('overridableMethod', function(){         
        expect(sut.overridableMethod()).toEqual('qux');
    });         

}); 

Example package.json (with code coverage enabled for test command):

{
  "name": "testDemoES6Jest",
  "version": "1.0.0",
  "main": "index.js",
  "repository": "https://github.com/stefaneidelloth/testDemoES6Jest.git",
  "author": "Stefan Eidelloth <matameko@posteo.de>",
  "license": "MIT",
  "dependencies": {},
  "devDependencies": {
    "@babel/core": "^7.5.5",
    "@babel/preset-env": "^7.5.5",
    "babel-jest": "^24.8.0",
    "jest": "^24.8.0",
    "jest-cli": "^24.8.0",
    "requirejs": "2.3.6"
  },
  "scripts": {
    "test": "jest --coverage --collectCoverageFrom src/**/*.js ",
    "debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --watch --runInBand"
  }
}

For debugging there are several options, for example:

a) VisualStudioCode, together with the Jest plugin

(Debug=>Install additional Debuggers=> Jest ("Use Facebook's Jest with Pleasure", https://github.com/jest-community/vscode-jest)

Example debug configuration launch.json:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [

        {
            "type": "node",
            "request": "launch",
            "name": "Jest Tests",
            "program": "${workspaceRoot}\node_modules\jest\bin\jest.js",
            "args": [
                "-i"
            ],           
            "internalConsoleOptions": "openOnSessionStart"           
        }
    ]
}

b) Google Chrome:

  • Run following console command:

    node --inspect-brk ./node_modules/jest/bin/jest.js

(can be saved as alias within your packages.json under scripts: {"debug": ... and run with npm run-script debug)

  • Open Chrome browser and enter the address

    chrome://inspect

  • Click on Open Dedicated DevTools for Node

  • Drag and drop your project directory to the dev tools to allow file access (only required once)

  • Open the file you would like to debug and set a break point

  • Click continue in the dev tools to continue to your wanted break point

Also see

c) Webstorm

See https://blog.jetbrains.com/webstorm/2018/10/testing-with-jest-in-webstorm/


For tests in browsers also see


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...