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

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]

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


当前回答

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

  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()

其他回答

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

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

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

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

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

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

从文档中可以看到:

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

例子:

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

在我的组件,我需要访问window.location.search。这是我在Jest测试中所做的:

Object.defineProperty(global, "window", {
  value: {
    location: {
      search: "test"
    }
  }
});

如果窗口属性在不同的测试中必须不同,我们可以将窗口模拟放到函数中,并使其可写,以便在不同的测试中重写:

function mockWindow(search, pathname) {
  Object.defineProperty(global, "window", {
    value: {
      location: {
        search,
        pathname
      }
    },
    writable: true
  });
}

并在每次测试后重置:

afterEach(() => {
  delete global.window.location;
});

下面的方法对我很有效。这种方法允许我测试一些在浏览器和Node.js中都可以工作的代码,因为它允许我将window设置为undefined。

这是笑话24.8(我相信):

let windowSpy;

beforeEach(() => {
  windowSpy = jest.spyOn(window, "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();
});

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

  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()