javascript - 如何更改 Jasmine 中包含的 Javascript 文件中定义的全局变量?

标签 javascript node.js jasmine

背景

过去几个小时我一直在试图找到这个问题的答案,但这可能是因为我对 require.js 缺乏了解。

这就是我正在处理的内容:我有一个紧密耦合的遗留企业 Web 应用程序,该应用程序于 2001 年编写,当时使用 IE 5 特定的 Javascript。名为 Menu.js 的 Javascript 文件具有使 HTML 表格能够像菜单一样工作的功能,其中表格单元格是菜单项,其中一些菜单项可以展开子菜单,这些子菜单是更多表格单元格。

简而言之,代码很糟糕。不过,我必须提供一个创可贴,使其与 Chrome 和 Firefox 兼容。解决方案是重写每个 Javascript 函数的内部结构以使用 jQuery,这样它将跨浏览器兼容。

每个网页都包含Menu.js,并且每个网页都是自动生成的,所以我被迫通过从Menu.js注入(inject)它来包含jQuery,它有以下代码:

Menu.js

function injectQuery() {
  const script = document.createElement('script');
  script.src = 'https://code.jquery.com/jquery-3.2.1.min.js';
  document.getElementByTagName('head')[0].appendChild(script);
}

injectJQuery();

function getSubMenu(el) {
  return $(el).find('table')[0] || null;
}

// Other functions are defined here as well

if ( typeof module !=== 'undefined' && module.hasOwnProperty('exports') ) {
  module.exports.getSubMenu = getSubMenu;
}

在 Jasmine 中,测试 Menu.js 时,我无法使用 $() 函数而不出现未定义的错误。为了解决这个问题,我有以下代码:

menu.spec.js

const sut = require('../src/js/Menu.js');
const testMenu = require('./resources/test.menu').data();

const { JSDOM } = require('jsdom');
const { window } = new JSDOM();
const { document } = window.window;
const jQuery = require('jquery')(window);

global.$ = jQuery;

describe('getSubMenu()', () => {
  beforeEach(() => {
    document.querySelector('body').innerHTML = testMenu;
  });

  it('should return the nested sub-menu-3 table given the sub-menu-2 element', () => {
    const subMenu2 = $('#sub-menu-2');
    const result = sut.getSubMenu(subMenu2);

    expect(result.id).toBe('sub-menu-3');
  });
});

问题

每当我运行规范时,都会收到以下错误:

const script = document.createElement('script');
                        ^
ReferenceError: document is not defined
    at injectJquery

因此,我不希望在 Jasmine 规范运行时运行 Menu.js 中定义的 injectJquery() 方法。我尝试了以下方法:

尝试 1 - 更改 Jasmine 中的全局值

Menu.js

var isUnderTest = false;

...

if (!isUnderTest) {
  injectJquery();
}

...

if ( typeof module !=== 'undefined' && module.hasOwnProperty('exports') ) {
  ...
  module.exports.isUnderTest = isUnderTest;
}

menu.spec.js 中,我尝试设置以下内容:

sut.isUnderTest = true;

但是,规范仍然尝试执行 injectJquery() 并且我收到了相同的错误。我尝试为 isUn​​derTest 变量定义一个 setter 函数并调用它,然后导出该函数,但我仍然收到相同的错误。

尝试 2 - 使用 Jasmine spy 强制函数不执行任何操作

我尝试使用 Jasmine spy 使该函数不执行规范中的任何操作,代码如下:

spyOn(sut, 'injectJquery').and.callFake(() => null);

我确实从 Menu.js 中导出了 injectJquery() 函数;然而,这也不起作用。

尝试 3 - 定义 Node 全局变量

我尝试在 menu.spec.js 中定义一个 Node 全局变量,如下所示:

global.isUnderTest = true;

但是,还是不行。

最终想法

来自 Java 世界并使用 JUnit,我希望我在 Jasmine 中定义的测试对象系统的行为与 Java 类似,尽管我知道 Javascript 文件 Menu.js ,不是一个对象。

我希望 injectJquery() 在浏览器中运行,但不在规范中运行。有什么方法可以抑制我没有看到的方法调用吗?

最佳答案

经过长时间的尝试和错误,我决定使用 hack-ish 的方式通过执行以下操作来解决问题:

try {
  injectJquery();
} catch (e) {
  if (e instanceof ReferenceError) {
    // The system is under test: do nothing
  }
}

希望这对使用 Jasmine 测试遗留 Javascript 的其他人有所帮助。

关于javascript - 如何更改 Jasmine 中包含的 Javascript 文件中定义的全局变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45872333/

相关文章:

javascript - DataTable 清除搜索过滤器并重新加载需要两次点击

javascript - 如何使用 JavaScript 在浏览器中存储文件

javascript - 当项目添加到 image.onload() 上的数组时,Angular 2 UI 不更新

node.js - 如何在微服务架构中进行身份验证和授权

node.js - css 未在所有带有express-handlebars 的路线上呈现

node.js - GetCursorPos Node FFI - 如何通过 ref 获取指针返回

javascript - 模拟 Angular 模型的 $http.put 结果 - 单元测试

javascript - 为什么我不能推送到 Vue 数组并让它工作?

javascript - 单元测试:Q.js 和 Jasmine.js

javascript - 无条件地使单元测试失败