Jest.js 如何使用react测试库和react-hooks在同一个React自定义钩子中测试两个不同的GET查询?

tct7dpnv  于 2023-06-04  发布在  Jest
关注(0)|答案(2)|浏览(155)

我使用一个自定义的react钩子来执行两个使用react query的GET操作。该API位于单独的模块中,并在useQuery中使用getTestByUid()和getTestStatuses()。

// TestHook.js
import { useQuery } from "react-query" 

export const useTest = (uid) => {
  const { data: test } = useQuery("test", () => getTestByUid(uid));
  const { data: testStatuses } = useQuery("statuses", () => getTestStatusesByUid(uid));
  
  return {
    test,
    testStatuses
  }
}

我的测试定义如下:

// test-hook.test.js
import { renderHook } from "@testing-library/react-hooks";
import { QueryClient, QueryClientProvider } from "react-query";
import { useTest } from "../src/hooks/TestHook";
import * as testApi from "../src/api/test-api.js";

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
    },
  },
});

const wrapper = ({ children }) => {
  return (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

describe("useTestHook", () => {
  it("should return a test", async () => {
    jest
      .spyOn(testApi, "getTestByUid")
      .mockImplementationOnce(() =>
        Promise.resolve({ data: { name: "secret test" } })
      );
    const { result, waitForNextUpdate } = renderHook(
      () => useTest("bb450409-d778-4d57-a4b8-70fcfe2087bd"),
      { wrapper: wrapper }
    );
    await waitForNextUpdate();
    expect(result.current.test).toBe({ name: "secret test" });
  });

  it("should return statuses for a test", async () => {
    jest.spyOn(testApi, "getTestStatusesByUid").mockImplementationOnce(() =>
      Promise.resolve({
        data: ["in_progress", "ready_for_approval", "rejected"],
      })
    );
    const { result, waitForNextUpdate } = renderHook(
      () => useTest("bb450409-d778-4d57-a4b8-70fcfe2087bd"),
      { wrapper: wrapper }
    );
    await waitForNextUpdate();
    expect(result.current.testStatuses).toBe([
      "in_progress",
      "ready_for_approval",
      "rejected",
    ]);
  });
});

第一个测试通过的很好,但是在第二个测试中,当我试图AsserttestStatuses值时,我得到了undefined返回。为什么在监视getTestStatusesByUid API调用时会发生这种情况?

xurqigkl

xurqigkl1#

您应该为每个测试用例模拟getTestStatusesByUidgetTestByUid,否则,测试用例不是孤立的。
这两个测试用例测试useTest钩子的返回值,因此没有必要将它们分开。一个测试用例就足够了。
此外,根据Assert语句中的预期值,两个API的模拟解析值都不正确。由于useQuery返回一个data字段来保存解析值,因此不需要将解析值 Package 到{ data: "mock resolved value" }中。
例如
api.js

export const getTestByUid = (uid) => { }
export const getTestStatusesByUid = (uid) => { }

test-hook.js

import { useQuery } from "react-query"
import { getTestByUid, getTestStatusesByUid } from "./api";

export const useTest = (uid) => {
  const { data: test } = useQuery("test", () => getTestByUid(uid));
  const { data: testStatuses } = useQuery("statuses", () => getTestStatusesByUid(uid));

  return {
    test,
    testStatuses
  }
}

test-hook.test.js

import { renderHook } from '@testing-library/react-hooks';
import { QueryClient, QueryClientProvider } from 'react-query';
import { useTest } from './test-hook';
import * as testApi from './api';
import React from 'react';

jest.mock('./api');

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      retry: false,
    },
  },
});

const wrapper = ({ children }) => {
  return <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>;
};

describe('useTestHook', () => {
  it('should return a test', async () => {
    testApi.getTestByUid.mockResolvedValue({ name: 'secret test' });
    testApi.getTestStatusesByUid.mockResolvedValue(['in_progress', 'ready_for_approval', 'rejected']);

    const { result, waitForNextUpdate } = renderHook(() => useTest('bb450409-d778-4d57-a4b8-70fcfe2087bd'), {
      wrapper,
    });

    await waitForNextUpdate();
    expect(result.current.test).toEqual({ name: 'secret test' });
    expect(result.current.testStatuses).toEqual(['in_progress', 'ready_for_approval', 'rejected']);
  });
});

测试结果:

PASS  stackoverflow/76369126/test-hook.test.jsx (25.099 s)
  useTestHook
    ✓ should return a test (35 ms)

--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |      60 |     100 |                   
 api.js       |     100 |      100 |       0 |     100 |                   
 test-hook.js |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        26.197 s

软件包版本:

"react-query": "^3.34.7",
"react": "^16.14.0",
"@testing-library/react-hooks": "^8.0.1",
6kkfgxo0

6kkfgxo02#

在第二个测试中,您使用jest.spyOn模拟getTestStatusesByUid函数,但似乎没有正确返回模拟的值。mockImplementationOnce函数需要一个返回模拟值的函数,但您传递的却是一个对象。
要解决这个问题,您需要将模拟值 Package 在函数中。以下是第二个测试的更新版本:

it("should return statuses for a test", async () => {
  jest.spyOn(testApi, "getTestStatusesByUid").mockImplementationOnce(() =>
    Promise.resolve({
      data: ["in_progress", "ready_for_approval", "rejected"],
    })
  );
  const { result, waitForNextUpdate } = renderHook(
    () => useTest("bb450409-d778-4d57-a4b8-70fcfe2087bd"),
    { wrapper: wrapper }
  );
  await waitForNextUpdate();
  expect(result.current.testStatuses).toEqual([
    "in_progress",
    "ready_for_approval",
    "rejected",
  ]);
});

在更新后的代码中,mockImplementationOnce函数接收一个返回模拟值的箭头函数。另外,请注意,我将期望值从toBe更改为toEqual,因为您正在比较数组,而toEqual执行深度比较。
通过这些更改,第二个测试现在应该正确Assert自定义钩子中testStatuses的值。

相关问题