如何模拟 ES6 单元测试的依赖关系? [英] How to mock dependencies for ES6 unit tests?

查看:18
本文介绍了如何模拟 ES6 单元测试的依赖关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

https://github.com/stefaneidelloth/testDemoES5

https://github.com/stefaneidelloth/testDemoES6

示例包括一个类 Qux,它继承自一个类 Baa.

在测试 Qux 时,我想模拟 Baa.

对于 ES5,我使用 Squire.js 来模拟 AMD 模块依赖项,并且单元测试工作正常.

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

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

<小时>

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

A. 如果我尝试使用 Squire 模拟已翻译的类 Baa (module 'src/baa') ...不再工作了.Webpack 将所有依赖项放在一个文件中,当使用 'src/qux' 时,我注入的 'src/baa' 不被考虑.

qux.test.js:定义(['乡绅'], 功能 (乡绅) {描述('Qux',函数(){变种;beforeEach(功能(完成){var injector = new Squire();injector.mock('src/baa', createBaaMock());注射器.需要(['src/qux'], 功能(qux模块){var Qux = quxModule.default;sut = new Qux('qux');完毕();});});它('quxMethod',函数(){期望(sut.quxMethod()).toEqual('quxMethod');});它('baaMethod',函数(){期望(sut.baaMethod()).toEqual('baaMockedMethod');});它('overridableMethod',函数(){期望(sut.overridableMethod()).toEqual('qux');});函数 createBaaMock(){var BaaMock = 函数(名称){this.name = 名称;};BaaMock.prototype.baaMethod = function () {返回'baaMockedMethod';}var moduleMock = {默认值:BaaMock}返回模块Mock;}});});

=> 我得到了错误

<块引用>

预期baaMethod"等于baaMockedMethod".

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

  • 使用 webpack 模式开发"而不是生产"以避免缩小

  • 启用 webpack 的 devtool 选项以启用源映射.这样,在调试时,原始代码也会显示在浏览器中.

<小时>

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

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

qux.test.js:

describe('Qux', function(){变种;beforeEach(功能(完成){需要(['inject!src/qux'],函数(ModuleInjector){var quxModule = ModuleInjector({'src/baa': crateBaaMock()});var Qux = quxModule.default;sut = new Qux('qux');完毕();});});它('quxMethod',函数(){期望(sut.quxMethod()).toEqual('quxMethod');});它('baaMethod',函数(){期望(sut.baaMethod()).toEqual('baaMockedMethod');});它('overridableMethod',函数(){期望(sut.overridableMethod()).toEqual('qux');});函数 createBaaMock(){var BaaMock = 函数(名称){this.name = 名称;};BaaMock.prototype.baaMethod = function () {返回'baaMockedMethod';}var moduleMock = {默认值:BaaMock}返回模块Mock;}});

=> 我得到了错误

<块引用>

Uncaught ReferenceError: module is not defined.

我也尝试了 mock-loader,但没有成功.

<小时>

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

<小时>

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

<小时>

E. 在回答 相关和旧的SO问题有人建议使用import * as obj"样式以模块的形式导入一个类,然后监视默认导出来模拟班上.

但是,如果多个测试使用该修改后的模块"(无法重新定义属性默认"),这可能会导致问题.

由于webpack没有动态加载依赖,下面的测试失败:

定义(['src/baa','src/qux'],功能(baa模块,qux模块){描述('Qux',函数(){变种;beforeEach(功能(完成){baaModule.default = createBaaMock();var Qux = quxModule.default;sut = new Qux('qux');完毕();});它('quxMethod',函数(){期望(sut.quxMethod()).toEqual('quxMethod');});它('baaMethod',函数(){期望(sut.baaMethod()).toEqual('baaMockedMethod');});它('overridableMethod',函数(){期望(sut.overridableMethod()).toEqual('qux');});函数 createBaaMock(){var BaaMock = 函数(名称){this.name = 名称;};BaaMock.prototype.baaMethod = function () {返回'baaMockedMethod';}var moduleMock = {默认值:BaaMock}返回模块Mock;}});});

<小时>

总而言之,我发现了很多过时和不完整的方法来测试 ES6 模块,但似乎没有一个效果很好.

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

=> 是否可以告诉 webpack 将翻译后的模块分开,以便我可以轻松地使用 Squire.js 注入依赖项?

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

相关内容:

解决方案

我从 karma 切换到 jest 和这里是一个工作演示项目:

https://github.com/stefaneidelloth/testDemoES6Jest

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

<小时>

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

从'./../src/qux.js'导入Qux;jest.mock('./../src/baa.js', () => {返回类 BaaMock {构造函数(名称){this.name = 名称;}baaMethod(){返回'baaMockedMethod';}}});描述('Qux',函数(){变种;之前(函数(){sut = new Qux('qux');});它('quxMethod',函数(){期望(sut.quxMethod()).toEqual('quxMethod');});它('baaMethod',函数(){期望(sut.baaMethod()).toEqual('baaMockedMethod');});它('overridableMethod',函数(){期望(sut.overridableMethod()).toEqual('qux');});});

<小时>

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

<代码>{"name": "testDemoES6Jest",版本":1.0.0",主":index.js",存储库":https://github.com/stefanidelloth/testDemoES6Jest.git","author": "Stefan Eidelloth <matameko@posteo.de>",许可证":麻省理工学院",依赖":{},开发依赖":{"@babel/core": "^7.5.5","@babel/preset-env": "^7.5.5","babel-jest": "^24.8.0","笑话": "^24.8.0","jest-cli": "^24.8.0",requirejs":2.3.6"},脚本":{"test": "jest --coverage --collectCoverageFrom src/**/*.js","调试": "节点 --inspect-brk ./node_modules/jest/bin/jest.js --watch --runInBand"}}

<小时>

调试几个选项,例如:

a) VisualStudioCode,连同 Jest 插件

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

示例调试配置launch.json:

<代码>{//使用 IntelliSense 了解可能的属性.//悬停查看现有属性的描述.//更多信息请访问:https://go.microsoft.com/fwlink/?linkid=830387版本":0.2.0",配置":[{类型":节点",请求":启动","name": "Jest 测试","程序": "${workspaceRoot}\node_modules\jest\bin\jest.js",参数":[-一世"],internalConsoleOptions":openOnSessionStart"}]}

b) 谷歌浏览器:

  • 运行以下控制台命令:<块引用>

    节点 --inspect-brk ./node_modules/jest/bin/jest.js

(可以在您的 packages.json 中的脚本下另存为别名:{"debug": ... 并使用 npm run-script debug 运行)

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

    <块引用>

    chrome://检查

  • 点击Open Dedicated DevTools for Node

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

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

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

另见

c) 网络风暴

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

<小时>

对于浏览器中的测试,另请参阅

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:

解决方案

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

这篇关于如何模拟 ES6 单元测试的依赖关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆