如何模拟用户创建的窗口属性?

8
我有一个JavaScript实用程序文件,用于进行API请求。它在顶部有两个条件语句,依赖于window.VarA和window.VarB。其中VarA和VarB是我们软件提供给窗口的变量。如果它们被设置了,就会运行条件内的代码。由于这个文件实际上不渲染窗口,我无法将这些属性设置为任何值,并获得条件内的代码覆盖率。
我尝试过渲染默认窗口,但似乎不起作用。我可以使用Jest和Enzyme。
我正在测试的对象的代码如下:
let param = "";
let url = "";
if(window.VarA !== underfined){
     param = window.VarA; <<--need coverage here
}
if(window.VarB !== underfined){
     url = window.VarB; <<--need coverage here
}

有没有一种方法可以为我的测试模拟这些窗口属性?

2个回答

9

Jest的默认测试环境是使用jsdom创建的类浏览器环境,提供了一个window对象。

如果测试环境是jsdom,那么可以通过windowglobal来访问window对象。

将变量设置到window上就像下面这样简单:

window.VarA = 'foo';  // global.VarA works as well
window.VarB = 'bar';  // global.VarB works as well

更加棘手的部分是在代码运行之前设置这些变量。
考虑以下config.js模块:
let param = "";
let url = "";
if (window.VarA !== undefined) {
  param = window.VarA;
}
if (window.VarB !== undefined) {
  url = window.VarB;
}

export const config = {
  param,
  url
}

如果您使用 require,那么代码直到模块被要求才会运行,这样就可以进行以下测试:
test('setting window variables', () => {
  window.VarA = 'foo';  // global.VarA works as well
  window.VarB = 'bar';  // global.VarB works as well

  const { config } = require('./config');  // now require the module

  expect(config.param).toBe('foo');  // SUCCESS
  expect(config.url).toBe('bar');  // SUCCESS
});

另一方面,如果您使用import,则变量需要在测试运行之前在window中定义,因为导入是被提升的,并且会在任何测试代码运行之前发生:

import { config } from './config';  // this runs first

test('setting window variables', () => {
  window.VarA = 'foo';  // <= this doesn't affect config
  window.VarB = 'bar';  // <= this doesn't affect config

  expect(config.param).toBe('foo');  // FAIL
  expect(config.url).toBe('bar');  // FAIL
});

因此,如果您使用 import,那么您需要使用类似于 setupFilesAfterEnv 的东西来在测试开始运行之前设置 window 变量:

// Configure Jest using setupFilesAfterEnv
// to run a module that sets the window variables
// so they are already set before the test begins...

import { config } from './config';

test('setting window variables', () => {
  expect(config.param).toBe('foo');  // SUCCESS
  expect(config.url).toBe('bar');  // SUCCESS
});

我还没有尝试过应用这个,但感谢您对我另一个问题的回复!你是个超级棒的人! - jwolsborn
非常感谢!!!我遇到了一个类似的问题,无法从并行的index.ejs文件中访问在index.js文件中定义的window.myVar。通过执行:“import index from'../../src/index.js'”,解决了我的问题,并使得测试可以访问window.myVar。再次感谢。 - Josh
1
我在setupTests.js文件中添加了global.myVar="myValue"。这会自动将myVar属性添加到window对象中。它起作用了! - Mounika Bathina

1

Brian Adams 给出了很好的答案。作为 `setupFilesAfterEnv` 的替代方案,您还可以在测试主体中动态导入模块:

test('setting window variables', async () => {
  window.VarA = 'foo';
  window.VarB = 'bar';
  const { config } = await import('./config');
  expect(config.param).toBe('foo');  // SUCCESS
  expect(config.url).toBe('bar');  // SUCCESS
});

请注意,window变量不能被重新赋值 - 进一步的测试仍将引用最初分配的值或产生副作用。例如:
test('setting window variables', async () => { ... });

test('setting a different window variable A', async () => {
  window.VarA = 'baz';
  const { config } = await import('./config');
  expect(config.param).toBe('baz');  // FAIL, it's still 'foo'
});

为避免精神混乱,值得将window分配移动到beforeAllbeforeEach中:

beforeAll(() => {
  window.VarA = 'foo';
  window.VarB = 'bar';
});

test('setting window variables', async () => {
  const { config } = await import('./config');
  expect(config.param).toBe('foo');
  expect(config.url).toBe('bar');   
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接