使用 Jest 在 JavaScript 中测试 window.open()

我最近必须为打开新浏览器窗口的 React 组件编写测试。为了打开新窗口,我在代码中使用了 window.open()。这使得组件易于编写,但我必须以不同的方式思考如何为此编写测试。

有关 window.open() 方法的更多信息请参阅 mdn web 文档。

为了设置位或背景,我使用了一个 React 组件,该组件包含一个带有几个输入的简单表单。当用户完成输入并提交表单时,它会打开一个指向指定 URL 的新窗口,并将输入作为 URL 参数。

要测试的组件

以下是该组件的一个非常简化的版本,仅供演示。我建议使用类似 react-hook-form 的东西来为表单添加验证。

// MyForm.js
import React, { useState } from "react";

const MyForm = ({ baseURL }) => {
  const [name, setName] = useState("");
  const [subject, setSubject] = useState("");

  const onSubmit = () => {
    window.open(
      `${baseURL}?name=${encodeURIComponent(name)}&subject=${encodeURIComponent(
        subject
      )}`,
      "_blank"
    );
  };

  return (
    
setName(e.target.value)} /> setSubject(e.target.value)} />
); }; export default MyForm;

现在我们有了组件,让我们考虑对它进行测试。

我通常会测试什么

通常我会测试我的组件中呈现的内容,使用断言,例如期望组件具有文本内容或断言 url 是预期的(使用 window.location.href),但我很快意识到这种方法对于这个例子来说是行不通的。

Window.open 打开一个新的浏览器窗口,因此它不会影响我们正在测试的组件。我们看不到新窗口里面有什么,也看不到它的 URL 是什么,因为它超出了我们正在测试的组件的范围。

那么我们如何测试我们无法看到的东西呢?我们实际上不需要测试是否打开了新窗口,因为那样会测试窗口界面的功能而不是我们的代码。相反,我们只需要测试是否调用了 window.open 方法。

模拟 window.open()

因此我们需要模拟 window.open() 并测试它是否在我们的代码中被调用。

// Mock window.open
global.open = jest.fn();

现在我们可以设置输入中的值,提交表单,然后测试是否调用了 window.open。我们可以使用 fireEvent 来设置输入的值并按下提交按钮。

fireEvent.input(screen.getByLabelText("Name"), {
  target: {
    value: "Test Name",
  },
});
fireEvent.input(screen.getByLabelText("Subject"), {
  target: {
    value: "An example subject",
  },
});
fireEvent.submit(
  screen.getByRole("button", { name: "Submit (opens in new window)" })
);

值得一读文档,了解 fireEvent 的注意事项。根据你的使用情况,你可能希望使用用户事件。

我们希望等待方法运行。我们可以使用 waitFor() 来实现。

await waitFor(() => {
  expect(global.open).toHaveBeenCalled();
});

为了确保我们不会打开大量新窗口,我们可以检查只调用一次 window.open。

await waitFor(() => {
  expect(global.open).toHaveBeenCalledTimes(1);
});

我们还可以检查调用该方法的参数,将我们期望的 URL 作为第一个参数传递,将目标作为第二个参数传递。

await waitFor(() => {
  expect(global.open).toHaveBeenCalledWith(
    "http://example.com?name=Test%20Name&subject=An%20example%20subject",
    "_blank"
  );
});

完整的测试文件

这是完整的测试文件,供您参考。

// MyForm.test.js
import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import MyForm from "./MyForm";

describe("MyForm test", () => {
  beforeEach(() => {
    // Mock window.open
    global.open = jest.fn();
  });

  it("opens a new window with the correct url", async () => {
    render();

    fireEvent.input(screen.getByLabelText("Name"), {
      target: {
        value: "Test Name",
      },
    });
    fireEvent.input(screen.getByLabelText("Subject"), {
      target: {
        value: "An example subject",
      },
    });
    fireEvent.submit(
      screen.getByRole("button", { name: "Submit (opens in new window)" })
    );

    await waitFor(() => {
      expect(global.open).toHaveBeenCalled();
      expect(global.open).toHaveBeenCalledTimes(1);
      expect(global.open).toHaveBeenCalledWith(
        "http://example.com?name=Test%20Name&subject=An%20example%20subject",
        "_blank"
      );
    });
  });
});

照片由 stockSnap 上的 energepic.com 拍摄