jest

2023/9/6

# 单测

  1. mock外部库引入函数返回值
const fileTypeMock = jest.fn().mockReturnValue({ type: "image" });

// 对象的这个方法执行后被返回指定值
jest.spyOn(Helper, "getFileType").mockImplementation(fileTypeMock);
1
2
3
4
  1. 更改外部库的某个函数
const mockNav = jest.fn();
const mockedUseNavigate = jest.fn().mockReturnValue(mockNav);
jest.mock("react-router-dom", () => ({
  ...(jest.requireActual("react-router-dom") as object),
  // mock某一个使用的函数,直接给返回值
  // useSearchParams: () => [new URLSearchParams({ sjmsgToken: "1", v: "1" })],
  useNavigate: () => mockedUseNavigate,
}));
1
2
3
4
5
6
7
8
  1. 执行hook
const { result, rerender } = renderHook(() => useFileDropzone(), {
  wrapper: MemoryRouter,
});
1
2
3
  1. 替代外部复杂方法
const createObjectURLMock = jest.fn();
URL.createObjectURL = createObjectURLMock;
1
2
  1. 执行hook返回的函数
const res = result.current.acceptedFilesFormat(filesMock);
1
  1. 替代ref
// mock
const idInput: any = { value: "testUser" };
// 执行hook
const { result, rerender } = renderHook(() => useLogin(), {
  wrapper: MemoryRouter,
});
// 给ref赋值并等待执行异步函数
await act(async () => {
  if (result.current.idRef) {
    result.current.idRef.current = idInput;
  }
  if (result.current.pwdRef) {
    result.current.pwdRef.current = pwdInput;
  }
  await result.current.login();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  1. 断言函数被执行
expect(Service.loginService).not.toHaveBeenCalled();
1
  1. 导入外部常量
jest.mock("utils/constants.ts", () => ({
  ...(jest.requireActual("utils/constants.ts") as object),
  INTERVAL_TIME: 1,
}));
1
2
3
4
  1. 重复渲染
  • 执行一遍,rerender
  • 修改参数,rerender
  • 断言结果,要用result.current.xxx
act(() => {
  const {
    replyContentTopRef,
    replyContentFootRef,
    contentTopHeight,
    contentFootHeight,
  } = result.current;
  if (replyContentTopRef) {
    replyContentTopRef.current = { clientHeight: 100 } as any;
  }
  if (replyContentFootRef) {
    replyContentFootRef.current = { clientHeight: 100 } as any;
  }
  rerender();
});

act(() => {
  props.fixed = false;
  rerender();
});

// ASSERT:確認
expect(result.current.contentTopHeight).toEqual(100);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  1. 单个it修改外部库导入函数
// 全局更改一个默认的,让此库变为可修改的
jest.mock("react-router-dom", () => ({
  ...(jest.requireActual("react-router-dom") as object),
  useParams: jest.fn().mockReturnValue({}),
}));

// 单个it自己修改(不修改也要默认给一个)
it(() => {
  jest.spyOn(RouterDom, "useParams").mockReturnValue({});
})
1
2
3
4
5
6
7
8
9
10
  1. 防抖单测

it("change searchText should debounce fetch tip list", async () => {
  // 模拟时间流逝
  jest.useFakeTimers();

  // ARRANGE:準備

  // ACT:実行
  const { result, rerender } = renderHook(() => useRecipientSelect(), {
    wrapper: MemoryRouter,
  });
  await act(async () => {
    result.current.onMenuClick();
    rerender();
  });

  await act(async () => {
    result.current.changeInputText("xxx");
    rerender();

    // 模拟时间流逝 1000
    await jest.advanceTimersByTime(1000);
    
    // await jest.runAllTimers();
  });

  // ASSERT:確認
  expect(result.current.tipList).toEqual(1);

  // 关闭模拟时间流逝
  jest.useRealTimers();
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  1. 清理函数
beforeEach(() => {
  jest.clearAllMocks();
});
1
2
3
  1. 确认时异步等待
await waitFor(() => {
  expect(mockSetEditing).toBeCalled();
});
1
2
3
上次更新: 9/17/2024