如何模拟 ES6 单元测试的依赖关系?

How to mock dependencies for ES6 unit tests?

我有一些 ES5 示例项目,我想将其转换为 ES6:

https://github.com/stefaneidelloth/testDemoES5

https://github.com/stefaneidelloth/testDemoES6

示例包括 class Qux 继承自 class Baa.

测试Qux时,我想模拟Baa

对于 ES5 我使用 Squire.js 来模拟 AMD 模块依赖和单元测试 工作很好.

遗憾的是我找不到直接支持ES6(="ECMAScript 2015 Language"、ES2015)模块的测试框架。我们现在已经 2020 年了,仍然没有 ES2015 的单元测试?我已经花了很多时间来尝试让这些测试正常工作……而且我的印象是我的方法遗漏了一些东西。

由于找不到对ES6测试的直接支持,我尝试坚持karma并使用webpack将ES6模块代码翻译成ES5 AMD模块进行测试


让我们首先考虑将 karma 与已转换为 AMD 模块的 requirejs 和 ES6 代码结合使用。

A. 如果我尝试用 SquireclassBaa(模块 'src/baa')模拟翻译的 classBaa(模块 'src/baa') =138=] ... 不再起作用了。 Webpack 将 所有依赖项放在一个文件中 并且当使用 'src/qux' 时,我注入的 'src/baa' 不被考虑。

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;
        }       

    }); 

});

=> 我收到错误

Expected 'baaMethod' to equal 'baaMockedMethod'.

一些关于调试的更多信息....转换为 ES5 的缺点是在调试测试时,在浏览器中运行的代码看起来与原始代码不同(默认情况下)。因此,可能的错误更难识别。有帮助的是:

  • 使用 webpack 模式 "development" 而不是 "production" 以避免缩小

  • 启用。这样调试的时候浏览器中也能看到原来的代码。


B. 我尝试使用 inject-loader,替代 Squire.js,它了解 webpack: https://github.com/LeanKit-Labs/inject-loader

但是,这似乎是一个与我的 karma + requirejs 项目不兼容的 CommonJs 模块:

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;
    }       

}); 

=> 我收到错误

Uncaught ReferenceError: module is not defined.

我也试过 mock-loader 但没成功。


C. 我尝试不使用 AMD 模块,而是使用 CommonJs 模块作为 webpack 编译的目标。但是,我没有设法将 commonjs proprocessor 和 karma 的 webpack 预处理器一起使用。


D. 我尝试使用 system.js 而不是 require.js 和 webpack with Karma。然而,karma-system.js 依赖于一个非常旧的 system.js 版本(0.19.47),我没有让它工作。


E. 在对 related and old SO question 的回答中,有人建议使用 "import * as obj" 样式以模块形式导入 class然后 监视默认导出 以模拟 class。

但是,如果多个测试都在使用 "modified module"(无法重新定义 属性 "default"),这可能会导致问题。

由于 webpack 不会动态加载依赖项,因此以下测试失败:

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;
        }       

    }); 

}); 

总而言之,我发现了很多过时和不完整的测试 ES6 模块的方法,其中 none 似乎效果很好。

=> 如果我应该继续使用 karma,我需要如何调整我的测试 qux.test.js 示例代码(可能还有我的配置文件)以正确模拟classBaa?

=> 是否可以告诉 webpack 将翻译的模块分开,这样我就可以轻松地注入依赖关系 Squire.js?

=> 是否有更好和最新的工作 flow/framework 用于在浏览器中对 ES6 模块进行单元测试?有人尝试将玩笑与业力结合起来吗?

相关资料:

我从 karma 切换到 jest,这是一个有效的演示项目:

https://github.com/stefaneidelloth/testDemoES6Jest

工作流程仍然基于转译器 (babel),但这发生在后台,并不会真正影响开发体验。


模拟某些 ES6 模块的示例测试代码:

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');
    });         

}); 

示例package.json(为测试命令启用了代码覆盖率):

{
  "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"
  }
}

为了调试several options,例如:

a) VisualStudioCode,连同 Jest 插件

(调试=>安装额外的调试器=> Jest ("Use Facebook's Jest with Pleasure", https://github.com/jest-community/vscode-jest)

示例调试配置 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:

  • 运行 以下控制台命令:

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

(可以在脚本下的 packages.json 中保存为别名:{"debug": ... and 运行 with npm 运行-脚本调试)

  • 打开Chrome浏览器并输入地址

    chrome://inspect

  • 单击打开节点专用开发工具

  • 将您的项目目录拖放到开发工具以允许文件访问(只需要一次)

  • 打开要调试的文件并设置断点

  • 在开发工具中单击继续以继续到您想要的断点

另见

c) Webstorm

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


对于浏览器中的测试,另见