【问题标题】:`window` is not being exposed to Jest`window` 没有暴露给 Jest
【发布时间】:2018-02-16 17:42:27
【问题描述】:

我有一个导入组件的测试,该组件又导入一个帮助文件,该文件使用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 之类的全局对象暴露给一个笑话测试?

相关的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"
  }

【问题讨论】:

  • ^^ 因为我真的搞砸了markdown 赏金描述中的链接,这是the enzyme docs的链接
  • 你用的是哪个版本的 jest?
  • @JoseAPL jest@^20.0.4, react@16.0.0
  • 能否请您添加您的package.json,默认情况下,jest 使用testEnvironment 到类似浏览器的jsdom,因此您不必将jsdom 导入到您的单元测试中
  • @JoseAPL 我已经从 package.json 添加了相关部分

标签: javascript reactjs unit-testing jestjs


【解决方案1】:

你可以试试

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

【讨论】:

    【解决方案2】:

    你可以简单地模拟location

    global.location = {search: 'someSearchString'}
    

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

    请注意,这仅在您的模块在测试完成导入所有模块后调用window.location 时才有效。

    export default () => window.location
    

    如果你的模块看起来像这样:

    const  l = window.location
    export default l 
    

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

    【讨论】:

    • 当我将此对象添加到我的测试顶部时:global.window = { location: { search: { api: 'fixture' } } } 我仍然收到ReferenceError: window is not defined 当我包含自己的import { Unwrapped as UnwrappedMyComponent } from '../MyComponent' 包含另一个使用window.location.search 的脚本的import .我已经在文件顶部尝试过它,也在我的名为 imports 的酶下方尝试过。我做错了什么吗?
    • 不,如果导入的模块在其初始化代码中使用window,则无法设置。在这种情况下,我会模拟出这种依赖关系。
    • 你是什么意思?能举个例子吗?
    • 更新了我的答案
    • 请注意,这仅在您的模块在测试完成导入所有模块后调用 window.location 时才有效。 > 我不确定我是否理解您的意思意思是这个或我如何控制它。 在这种情况下,您可以使用 jest.mock 模拟模块您在谈论this package吗?
    【解决方案3】:

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

    似乎贡献者之一声明他不打算在 jest 环境下将 jsdom 暴露给全局。

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

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

    祝你好运!

    【讨论】:

    • 您能否提供一个示例来说明如何在测试中使用它。照原样,我仍然得到参考错误。
    • 这解决了我的问题,只需在测试的beforeEach() 中添加即可
    【解决方案4】:

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

    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 = '&lt;!doctype html&gt;&lt;html&gt;&lt;body&gt;&lt;div id="root"&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;'; global.document = jsdom(documentHTML); global.window = document.parentWindow; import { Unwrapped as UnwrappedMyComponent } from '../MyComponent'; (MyComponent 使用window) => ReferenceError: window is not defined
    【解决方案5】:

    更新:

    正如@RiZKiT 在下面的评论中所提到的,因为 Jest v.27.0 the default test environment has changed"jsdom""node"

    您的问题取决于配置。

    在你设定的那一刻:

    "testEnvironment": "jest-environment-node"
    

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

    要解决此问题,请将 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] # 默认值:“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 并设置全局变量,jsdom 将自动模拟DOM 实现。

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

    【讨论】:

    【解决方案6】:

    不确定,但我认为您可以使用 jest.fn() 来做到这一点

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

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

    【讨论】:

      【解决方案7】:

      在我的例子中,我使用 rails 将自定义 env 文件的值注入到 vuejs 组件中。因此,我在开玩笑的快照中获得了未定义资产的基本 url。解决方案是在 jest.config.js -> setupFiles 中包含一个设置文件,如下所示。

      jest.config.js

      {
        setupFiles: [
          "./app/javascript/tests/testsConfig.js",
        ],
      }
      

      testsConfig.js

      process.env.BASE_DOMAIN = process.env.BASE_DOMAIN || 'http://localhost:3000/'
      
      window.TestsConfig = {
        "CLOUD_FRONT_BASE_URL": "https://cdn.site.com"
      }
      

      【讨论】:

        猜你喜欢
        • 2011-09-07
        • 1970-01-01
        • 2016-08-17
        • 2023-01-28
        • 2021-02-04
        • 2021-06-10
        • 2021-08-13
        • 2011-04-02
        • 1970-01-01
        相关资源
        最近更新 更多