在require.js中模拟全局窗口对象

Sco*_*ott 17 window mocking requirejs

  1. 我有一个使用window.applicationCache进行离线访问的Web应用程序.
  2. 我管理appCache的所有代码(例如,检查/更新/交换缓存)都封装在"缓存控制器"对象中.
  3. 我有单元测试来测试我的"缓存控制器"功能.为了测试,我暂时用我自己的模拟版本替换原生window.applicationCache对象(因为我只想测试我的代码,而不是浏览器appCache实现),例如.

    window.applicationCache = { /* my appCache mock */ };
    // unit tests run here. Code under test references window.applicationCache.
    
    Run Code Online (Sandbox Code Playgroud)

不久之前(大约是Chrome 16),这种方法非常有效.然后,Mac和Windows平台上的Chrome 17都删除了修补浏览器默认window.applicationCache属性的功能(虽然奇怪的是,它适用于Chrome for Linux适用于所有版本,包括Chrome 26).当时,我为此记录了一个Chromium bug ; 但不幸的是,错误报告仍被列为"未经证实".

无论如何,我只是将我的应用程序从传统的'browser globals'(即通过脚本标签加载*.js文件;所有JS对象都是全局的)移植到AMD风格的模块,使用require.js作为模块加载器.

AMD(或CommonJS)的一个好处是依赖注入,其中您的代码获取对任何依赖对象的本地引用,而不是依赖于全局引用,例如.

require(['foo'], function(Foo) {
  var bar = new Foo();
});
Run Code Online (Sandbox Code Playgroud)

...这使得对象模拟变得容易,因为您可以配置模块加载器在测试模式下为'foo'传递模拟对象.

我希望通过转移依赖注入,我可以解决我的applicationCache问题(因为传递到我的模块的'window'引用可以是全局窗口对象,也可以是模拟对象).

但是我不确定如何让require.js将'window'作为依赖项注入我的模块中?

是否有可能(可能使用shim配置?)来定义"窗口"模块; 然后可以将其传递给在全局"窗口"对象上运行的任何代码?所以我可以这样做:

require(['???'], function(window) {
  // 'window' here is the real window object, or for testing it's a mock window object
  window.applicationCache.update();
});
Run Code Online (Sandbox Code Playgroud)

......哪里'???' 是一个引用窗口对象的模块名称.

或者我是否需要定义我自己的导出"窗口"的模块,该窗口可以不同地映射单元测试,例如.

// window.js
define(function() {
  return window;  // real global window object
});

// window-mock.js
define(function() {
  return {
    applicationCache: { /* mock version of appCache */ }
  }
});

// for unit testing, remap 'window' to the mock version
require.config({
  map: {
    "cache-controller": {
      "window": "window-mock"
    }
  }
});

// cache-controller.js
require(['window'], function(window) {
  window.applicationCache.update();
});
Run Code Online (Sandbox Code Playgroud)

Sco*_*ott 6

我回答了自己的问题.我决定创建如上所述的window.js和window-mock.js模块,这允许我在运行单元测试时传递模拟版本,并在正常运行时使用"真实"窗口对象.