`window` 没有暴露给 Jest

IT技术 javascript reactjs unit-testing jestjs
2021-04-20 04:47:13

我有一个测试导入一个组件,该组件又导入一个帮助文件,该文件使用该window对象提取查询字符串参数。我收到以下错误window

 FAIL  src/js/components/__tests__/Controls.test.jsx
  ● Test suite failed to run

    ReferenceError: window is not defined

Controls.jsx

import { Unwrapped as Controls } from '../Controls'

describe('<MyInterestsControls />', () => {
  it('should render the component with the fixture data', () => {
    const component = shallow(
      <UnwrappedMyInterestControls
        dashboardData={dashboardData}
        loadingFlags={{ controls: false }}
      />
    )
    expect(component).toMatchSnapshot()
  })
})

Controls.jsx导入./helpers/services.js,其中包含以下内容:

import * as queryString from 'query-string'
const flag = queryString.parse(window.location.search).flag || 'off'
                               ^^^^^^ this seems to be the problem

我试图导入 jsdom

import { JSDOM } from 'jsdom'

和落实提出的解决方案在这里,在我的测试文件的顶部:

const { JSDOM } = require('jsdom');

const jsdom = new JSDOM('<!doctype html><html><body></body></html>');
const { window } = jsdom;

function copyProps(src, target) {
  const props = Object.getOwnPropertyNames(src)
    .filter(prop => typeof target[prop] === 'undefined')
    .map(prop => Object.getOwnPropertyDescriptor(src, prop));
  Object.defineProperties(target, props);
}

global.window = window;
global.document = window.document;
global.navigator = {
  userAgent: 'node.js',
};
copyProps(window, global);

但是我仍然收到错误消息,并且似乎 JSDOM 的窗口对象没有暴露在测试中。

我怎样才能正确曝光像全局对象windowdocument一个玩笑测试?

相关的package.json
  "scripts": {
    "test:watch": "NODE_ENV=test jest --watch"
  },
  ...
  "devDependencies": {
    ...
    "jest": "^20.0.4",
    "jest-mock": "^21.2.0",
    "jsdom": "^11.0.0",   
    ...  
  },
  ...
  "jest": {
    "verbose": true,
    "collectCoverageFrom": [
      "src/js/helpers/preparePayload.js",
      "src/js/components-ni",
      "!**/node_modules/**",
      "!**/dist/**"
    ],
    "coverageThreshold": {
      "global": {
        "statements": 50,
        "branches": 50,
        "functions": 50,
        "lines": 75
      }
    },
    "testEnvironment": "jest-environment-node"
  }
6个回答

更新:

正如在下面评论@RiZKiT提到的,由于玩笑v.27.0 默认的测试环境发生了变化,从"jsdom""node"

您的问题取决于配置。

在您设置的那一刻:

"testEnvironment": "jest-environment-node"

您正在将默认配置从类似浏览器的 jest 更改为jest-environment-node (类似节点),这意味着您的测试将在NodeJs环境下运行

要解决它,您可以将您设置testEnvironmentjsdom
或者您testEnvironment从您的配置中删除 ,以便它在您的配置中采用默认值package.json

 ...
  "jest": {
    "verbose": true,
    "collectCoverageFrom": [
      "src/js/helpers/preparePayload.js",
      "src/js/components-ni",
      "!**/node_modules/**",
      "!**/dist/**"
    ],
    "coverageThreshold": {
      "global": {
        "statements": 50,
        "branches": 50,
        "functions": 50,
        "lines": 75
      }
    }
  }

这是他们在文档中所说的:

testEnvironment [string] # 默认值:"jsdom"

将用于测试的测试环境。Jest 中的默认环境是通过 jsdom 的类似浏览器的环境。如果您正在构建节点服务,则可以使用节点选项来代替使用类似节点的环境。


你需要`node`环境吗?

正如我所看到的,您的测试旨在在类似浏览器的环境下运行。

如果您需要一个明确的节点环境,最好使用@jest-environment以下方法隔离这种情况

/**
 * @jest-environment node
 */

test('use node in this test file', () => {
  expect(true).not.toBeNull();
});

或者反过来,如果您打算在node环境下运行测试

/**
 * @jest-environment jsdom
 */

test('use jsdom in this test file', () => {
  const element = document.createElement('div');
  expect(element).not.toBeNull();
});

结论

有了这个,您可以避免jsdom手动导入和设置全局变量,jsdomDOM自动模拟实现。

如果您需要更改测试环境,请使用该符号 @jest-environment

由于 Jest 27“jsdom”不再是默认值而是“节点”,请参见github.com/facebook/jest/pull/9874jestjs.io/blog/2021/05/25/jest-27#flipping-defaults .所以你现在必须明确设置“jsdom”。
2021-06-19 04:47:13

你可以尝试做

global.window = new jsdom.JSDOM().window;
global.document = window.document;

https://github.com/facebook/jest/issues/2460#issuecomment-324630534

似乎其中一位贡献者宣称他不打算在 jest 环境下将 jsdom 暴露给全局。

但是,您可以使用Object.defineProperty(window, 'location', {value: '…'}API 来处理它,就像 Facebook 中的开发人员所做的那样在你的情况下,它可能是这样的:

    Object.defineProperty(window, 'location', {
      value: {
        search: ...
      },
    })

祝你好运!

您能否提供一个示例,说明如何在测试中使用它。照原样,我仍然收到参考错误。
2021-06-03 04:47:13
这解决了我的问题,只需要beforeEach()在测试中添加它
2021-06-21 04:47:13

您可以在此处找到有关如何执行此操作的示例:

https://www.codementor.io/pkodmad/dom-testing-react-application-jest-k4ll4f8sd

例如:

import {jsdom} from 'jsdom';

const documentHTML = '<!doctype html><html><body><div id="root"></div></body></html>';
global.document = jsdom(documentHTML);
global.window = document.parentWindow;
import { jsdom } from 'jsdom'; const documentHTML = '<!doctype html><html><body><div id="root"></div></body></html>'; global.document = jsdom(documentHTML); global.window = document.parentWindow; import { Unwrapped as UnwrappedMyComponent } from '../MyComponent';(MyComponent 使用window) =>ReferenceError: window is not defined
2021-06-10 04:47:13

你可以简单地模拟location

global.location = {search: 'someSearchString'}

另请注意,global在您的测试中是要测试的文件的全局上下文 ( global === window)

请注意,这仅在您的modulewindow.location在测试完成导入所有module后进行调用时才有效

export default () => window.location

因此,如果您的module如下所示:

const  l = window.location
export default l 

不起作用。在这种情况下,您可以使用jest.mock.

不,如果导入的modulewindow在其初始化代码中使用,则无法设置它。在这种情况下,我会模拟这种依赖关系。
2021-05-31 04:47:13
更新了我的答案
2021-05-31 04:47:13
请注意,这仅在您的module在测试完成导入所有module后进行 window.location 调用时才有效。> 我不确定我是否理解您的意思或我如何控制它。在这种情况下,你可以使用 jest.mock 来模拟module你是在谈论这个包吗?
2021-06-01 04:47:13
你是什​​么意思?你能给我举个例子吗?
2021-06-02 04:47:13
当我将这个对象添加到我的测试顶:global.window = { location: { search: { api: 'fixture' } } }我仍然收到ReferenceError: window is not defined当我import { Unwrapped as UnwrappedMyComponent } from '../MyComponent'包含本身包含的import另一个脚本的用途window.location.search我已经在文件的顶部和我的酶命名导入的下方尝试过它。我做错了什么吗?
2021-06-13 04:47:13