我需要测试一个在浏览器中打开一个新选项卡的功能

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

我想模拟窗口的打开函数,这样我就可以验证正确的URL被传递给打开函数。

使用笑话,我不知道如何模拟窗口。我试着设置窗口。使用mock函数打开,但这种方式不起作用。下面是测试用例:

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

但是它给出了误差

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

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

我应该对测试用例做什么?


当前回答

我试过一个类似的测试,对我很有效……

我的代码:

export const Blah = () => {
        const BLAH = 'https://www.google.com/'
        const handleBlah = () => {
            window.open(BLAH, '_blank')
        }

    return (
        <button onClick={handleBlah}> BLAHBLAH </button>
    )
}

我的测试使用Jest:

it('should be able to render "BLAHBLAH " button ', () => {
    window.open = jest.fn();
    const BLAH = 'https://www.google.com/'
    const { getByText } = render(<Blah/>) // Get text by my page Blah
    const buttonGoToBlah = getByText('BLAHBLAH') // Get button by text
    fireEvent.click(buttonGoToBlah) // Simulate the click event

    expect(window.open).toHaveBeenCalledTimes(1) // Expect the window.open have to been called at least once.
    expect(window.open).toHaveBeenCalledWith(BLAH, '_blank'); // And the page should be the same called in my BLAH page
})

其他回答

如果它类似于window.location.href中的窗口位置问题,则不能在测试中更改。#890,你可以尝试(调整):

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

我有一个实用函数,它允许我模拟窗口上的任何方法,如下所示:

  function givenMockWindowMethods(methods: Partial<{ [key in keyof Window]: jest.Mock<any, any> }>): () => void {
    const mocks = Object.values(methods);

    Object.entries(methods).forEach(([key, value]) => {
      Object.defineProperty(window, key, { value });
    });

    return (): void => mocks.forEach((mock) => mock?.mockClear());
  }

因此,如果我需要模拟窗口上的open方法(或任何东西),我可以这样做:

      const cleanupMocks = givenMockWindowMethods({ open: jest.fn() });
      // expect(...).toBe(...)

      //at the end of the test, clean it up
      cleanupMocks()

在Jest中有几种方法可以模拟全局变量:

Use the mockImplementation approach (the most Jest-like way), but it will work only for those variables which has some default implementation provided by jsdom. window.open is one of them: 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(); }); Assign the value directly to the global property. It is the most straightforward, but it may trigger error messages for some window variables, e.g. 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; }); Don't use globals directly (requires a bit of refactoring) Instead of using the global value directly, it might be cleaner to import it from another file, so mocking will became trivial with Jest.

文件。/ . 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

export const windowOpen = window.open;

文件。/ testedFile.js

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

Jest中的窗口对象是自我嘲笑的

其他答案中没有提到的一件事是OP的评论:

使用笑话,我不知道如何模仿窗户。

窗口对象已经被模拟,并且可以开箱引用。

从文档中可以看到:

Jest附带jsdom,它模拟DOM环境,就像您在浏览器中一样。这意味着我们调用的每个DOM API都可以用在浏览器中观察到的相同方式观察到!

例子:

describe('i am a window', () => {
    it('has a window object', () => {
      expect(window).toBeTruthy(); // test will pass
    });
});

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

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

参考:setupFilesAfterEnv [array]