如何使用 Jest 模拟 JavaScript 窗口对象?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/41885841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 00:43:52  来源:igfitidea点击:

How to mock the JavaScript window object using Jest?

javascriptmockingjestjs

提问by danny

I need to test a function which opens a new tab in the browser

我需要测试一个在浏览器中打开一个新标签的功能

  openStatementsReport(contactIds) {
    window.open(`a_url_${contactIds}`);
  }

I would like to mock the window's open function so I can verify the correct URL is passed in to the open function.

我想模拟窗口的 open 函数,以便我可以验证正确的 URL 是否被传递到 open 函数。

Using Jest, I don't know how to mock the window. I tried to set window.open with a mock function but this way doesn't work. Below is the test case

使用 Jest,我不知道如何模拟窗口。我试图用模拟函数设置 window.open 但这种方式不起作用。下面是测试用例

it('correct url is called', () => {
  window.open = jest.fn();
  statementService.openStatementsReport(111);
  expect(window.open).toBeCalled();
});

but it gives me the error

但它给了我错误

expect(jest.fn())[.not].toBeCalled()

    jest.fn() value must be a mock function or spy.
    Received:
      function: [Function anonymous]

what should I do to the test case? any suggestions or hints are appreciated

我应该对测试用例做什么?任何建议或提示表示赞赏

回答by Andreas K?berle

Instead of windowuse global

而不是window使用global

it('correct url is called', () => {
  global.open = jest.fn();
  statementService.openStatementsReport(111);
  expect(global.open).toBeCalled();
});

you could also try

你也可以试试

const open = jest.fn()
Object.defineProperty(window, 'open', open);

回答by tvsbrent

A method that worked for me was the following. This approach allowed me to test some code that should work both in the browser and in Node, as it allowed me to set windowto undefined.

对我有用的方法如下。这种方法允许我测试一些应该在浏览器和 Node 中都可以运行的代码,因为它允许我windowundefined.

This was with Jest 24.8 (I believe):

这是 Jest 24.8(我相信):

let windowSpy;

beforeEach(() => {
  windowSpy = jest.spyOn(global, 'window', 'get');
});

afterEach(() => {
  windowSpy.mockRestore();
});

it('should return https://example.com', () => {
  windowSpy.mockImplementation(() => ({
    location: {
      origin: 'https://example.com'
    }
  }));

  expect(window.location.origin).toEqual('https://example.com');
});

it('should be undefined.', () => {
  windowSpy.mockImplementation(() => undefined);

  expect(window).toBeUndefined();
});

回答by Poh Zi How

We can also define it using globalin setupTests

我们也可以使用globalin定义它setupTests

// setupTests.js
global.open = jest.fn()

And call it using globalin the actual test:

global在实际测试中调用它:

// yourtest.test.js
it('correct url is called', () => {
    statementService.openStatementsReport(111);
    expect(global.open).toBeCalled();
});

回答by abhishek khandait

You can try this:

你可以试试这个:

import * as _Window from "jsdom/lib/jsdom/browser/Window";

window.open = jest.fn().mockImplementationOnce(() => {
    return new _Window({ parsingMode: "html" });
});

it("correct url is called", () => {
    statementService.openStatementsReport(111);
    expect(window.open).toHaveBeenCalled();
});

回答by serv-inc

If it's similar to the window location problem at https://github.com/facebook/jest/issues/890, you could try [adjusted]

如果它类似于https://github.com/facebook/jest/issues/890上的窗口位置问题,您可以尝试 [已调整]

delete global.window.open;
global.window = Object.create(window);
global.window.open = jest.fn();

回答by jmarceli

There are a couple of ways to mock globals in Jest:

在 Jest 中有两种模拟全局变量的方法:

  1. Use mockImplementationapproach (most Jest like way), but it will work only for those variables which has some default implementation provided by jsdom, window.openis one of them:
  1. 使用mockImplementation方法(最像 Jest 的方式),但它仅适用于那些具有由 提供的默认实现的变量jsdomwindow.open是其中之一:
test('it works', () => {
  // setup
  const mockedOpen = jest.fn();
  // without making a copy you will have a circular dependency problem
  const originalWindow = { ...window };
  const windowSpy = jest.spyOn(global, "window", "get");
  windowSpy.mockImplementation(() => ({
    ...originalWindow, // in case you need other window properties to be in place
    open: mockedOpen
  }));

  // tests
  statementService.openStatementsReport(111)
  expect(mockedOpen).toBeCalled();

  // cleanup
  windowSpy.mockRestore();
});
  1. Assign value directly to the global property, most straight forward but may trigger error messages for some windowvariables e.g. window.href.
  1. 将值直接分配给全局属性,最直接,但可能会触发某些window变量的错误消息,例如window.href.
test('it works', () => {
  // setup
  const mockedOpen = jest.fn();
  const originalOpen = window.open;
  window.open = mockedOpen;

  // tests
  statementService.openStatementsReport(111)
  expect(mockedOpen).toBeCalled();

  // cleanup
  window.open = originalOpen;
});
  1. Don't use globals directly (requires a bit of refactoring)
  1. 不要直接使用全局变量(需要一些重构)

Instead of using the global value directly it might be cleaner to import it from another file, so mocking will became trivial with Jest.

不是直接使用全局值,而是从另一个文件导入它可能更干净,因此使用 Jest 进行模拟将变得微不足道。

./test.js

./test.js

jest.mock('./fileWithGlobalValueExported.js');
import { windowOpen } from './fileWithGlobalValueExported.js';
import { statementService } from './testedFile.js';

// tests
test('it works', () => {
  statementService.openStatementsReport(111)
  expect(windowOpen).toBeCalled();
});

./fileWithGlobalValueExported.js

./fileWithGlobalValueExported.js

export const windowOpen = window.open;

./testedFile.js

./testedFile.js

import { windowOpen } from './fileWithGlobalValueExported.js';
export const statementService = {
  openStatementsReport(contactIds) {
    windowOpen(`a_url_${contactIds}`);
  }
}

回答by Jee Mok

I found an easy way to do it: delete and replace

我找到了一个简单的方法:删除和替换

describe('Test case', () => {
  const { open } = window;

  beforeAll(() => {
    // Delete the existing
    delete window.open;
    // Replace with the custom value
    window.open = jest.fn();
    // Works for `location` too, eg:
    // window.location = { origin: 'http://localhost:3100' };
  });

  afterAll(() => {
    // Restore original
    window.open = open;
  });

  it('correct url is called', () => {
    statementService.openStatementsReport(111);
    expect(window.open).toBeCalled(); // Happy happy, joy joy
  });
});

回答by stefan

In your jest config add setupFilesAfterEnv: ["./setupTests.js"], create that file and add the code you want to run before the tests

在您的 jest 配置中添加 setupFilesAfterEnv: ["./setupTests.js"],创建该文件并添加您要在测试之前运行的代码

//setupTests.js
window.crypto = {
   .....
};

Ref: https://jestjs.io/docs/en/configuration#setupfilesafterenv-array

参考:https: //jestjs.io/docs/en/configuration#setupfilesafterenv-array