如何在单元测试之间重置 requirejs 模块

how to reset a requirejs module between unit tests

我有一个 JavaScript 项目,我想观察 TDD 方法。我为此选择了 karma 框架和 requirejs 库,并遵循了 karma 文档 here.

中演示的示例

有一个单元测试文件的例子,它是:

define(['app', 'jquery', 'underscore'], function(App, $, _) {

    describe('just checking', function() {

        it('works for app', function() {
            var el = $('<div></div>');

            var app = new App(el);
            app.render();

            expect(el.text()).toEqual('require.js up and running');
        });

        it('works for underscore', function() {
            // just checking that _ works
            expect(_.size([1,2,3])).toEqual(3);
        });

    });

});

这种方法的主要问题是没有办法为每次测试清除App模块。因此,如果在一个 it 调用中 App 发生了一些变化(如闭包变量变化等),那么它们会影响其他 it 调用。

有人可以对此提出建议吗?这些都是如此流行的工具,我不敢相信没有人 运行 遇到这种情况。

所以,为了概括这个问题,我想就这些更具体的问题寻求答案:

  • is there any way to "reset" (clear) module in requirejs?

您可以使用 requirejs.undef(),但文档提到了一些陷阱。据我所知,没有调用可以用来表示 "unload this module and everything it depends on",这在复杂的应用程序中可能是需要的。

  • is there any better approach to run karma and requirejs, so that modules do not have any remains (side effects) of other tests (it function calls) on them ?

更好的方法是设计您的应用程序,使状态与您实例化的对象相关联,而不是与 模块 本身相关联。这就是我在我的一个大型应用程序的测试套件中所做的。 beforeEach 挂钩重置需要重置的内容并为每个测试重新实例化应用程序。

在 Google 的帮助下,我找到了解决方法。我不能说它是理想的,但它至少可以工作,并且在每个测试中,一个必需的模块处于 原始 状态。

首先,需要有这样的 main.js 测试文件,它几乎与 docs 中的相同,只是测试未定义为模块且未作为依赖项包含在内:

require.config({file
    baseUrl: '/base',
    paths: {},
    shim: {},
    deps: [],
    callback: window.__karma__.start
});

在主要的karma配置中必须存在这个(需要调整自己的路径)

files: [
    {pattern: 'src/**/*.js', included: false},
    'tests/unit/**/*.js'
],

并且在测试本身中,我们利用 jasmine 异步测试功能和 requirejs.undef 方法 "clearing"模块(这 将重置加载程序的内部状态以忘记模块的先前定义 ,如 docs 中所述)。

这个方法有一个含义,它不会重置其他模块,这些模块已经加载并依赖于重置模块,但这应该不是问题,如果以正确的方式编写他的测试,那就是他只测试一个模块(可能是一些父模块,子模块从中继承了一些行为),因此不难 undef 多个模块一个。

describe('Some/Module tests', function() {
    'use strict'
    var M

    beforeEach(function(done) {
        requirejs.undef('src/Some/GParentModule')
        requirejs.undef('src/Some/ParentModule')
        requirejs.undef('src/Some/Module')
        require(['src/Some/Module'], function(m) {
            M = m
            done()
        })
    })

    it('some description', function() {
        var i = 0

        for (var prop in M) {
            i++
        }
        expect(i).toEqual(1)
        expect(prop).toEqual('init')
        expect(M.init).toEqual(jasmine.any(Function))
    })
})

我已经检查过了,在 it 调用之间对模块的更改不会持续存在,所以我想,使用这种方法是安全的。

谢谢大家的帮助,如果有人有更好的方法,请在这里回答。