`window`没有暴露给Jest

时间:2017-09-07 21:49:34

标签: javascript reactjs unit-testing jestjs

我有一个导入组件的测试,该组件又导入一个使用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'

并在我的测试文件顶部实现提交的here解决方案:

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之类的全局对象公开给jest测试?

相关的 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 个答案:

答案 0 :(得分:13)

您的问题依赖于配置。

在您设置的那一刻:

"testEnvironment": "jest-environment-node"

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

要解决此问题,请将testEnvironment设置为jsdom 或者您从配置中移除了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]#默认:&#34; jsdom&#34;

     

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

您是否需要`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并设置全局变量,jsdom将自动模拟DOM实现。

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

答案 1 :(得分:3)

您可以尝试

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

答案 2 :(得分:3)

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

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;

答案 3 :(得分:1)

你可以简单地模仿location

global.location = {search: 'someSearchString'}

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

请注意,只有在测试完成导入所有模块后模块进行window.location调用时,此方法才有效。

export default () => window.location

因此,如果您的模块如下所示:

const  l = window.location
export default l 

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

答案 4 :(得分:1)

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

似乎有一个贡献者宣称他没有计划在开玩笑的环境下将jsdom暴露给全球。

但是,您可以使用Object.defineProperty(window, 'location', {value: '…'} API来处理它,like the developer in Facebook do。在你的情况下,它可能像:

    Object.defineProperty(window, 'location', {
      value: {
        search: ...
      },
    })
祝你好运!

答案 5 :(得分:1)

不确定,但我认为你可以用jest.fn()

来做
global.window = jest.fn(() => {
  location: { ... }
})

甚至可能是window = jest.fn(...)