reactjs 在使用React测试库时,是否有任何解决方案可以模拟React查询的useQuery和useMutation

4jb9z9bj  于 2023-01-12  发布在  React
关注(0)|答案(6)|浏览(177)

我正在用react测试库编写测试用例。为了编写测试用例,我需要模拟react查询的useQuery和useMutation方法。如果有人知道解决方法,请指导我。我在这里添加相关代码。

工作区详细信息部分.test.tsx

import React from 'react'
import '@testing-library/jest-dom'
import '@testing-library/jest-dom/extend-expect'
import { screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { WorkspaceDetailsSection } from '../WorkspaceDetailsSection'
import { render } from '../../_utils/test-utils/test-utils'

const mockedFunction = jest.fn()

let realUseContext: any
let useContextMock: any

// Setup mock
beforeEach(() => {
  realUseContext = React.useContext
  useContextMock = React.useContext = jest.fn()
})
// Cleanup mock
afterEach(() => {
  React.useContext = realUseContext
})

jest.mock('../data', () => ({
  useMutationHook: () => ({ mutate: mockedFunction })
}))

const WorkspaceContext = {
  workspaceInfo: {
    name: 'name',
    dot: 'name',
    type: 'type'
  }
}
test('renders section with the valid details', async () => {
  useContextMock.mockReturnValue(WorkspaceContext)

  render(<WorkspaceDetailsSection />)

  expect(screen.getByText('Workspace name:')).toBeInTheDocument()
})

工作区详细信息部分.tsx

import React, { useContext } from 'react'

import { FormModal, useDisclosure } from '@chaine/keychaine'
import { workspaceService } from './data'
import { useMutation } from 'react-query'
import { Section } from '../account-settings'
import { Toast } from '../_shared/components'
import { IWorkspace } from '../_shared/refactoredInterfaces'
import constant from '../_shared/constants/message'
import { WorkspaceContext } from './WorkspacePage'
import capitalizeFirstLetter from '../_utils/capitalizeFirstLetter'
import { queryClient } from '../_shared/infra'
/**
 *
 * should import section component and return that
 */
export const WorkspaceDetailsSection = () => {
  const { isOpen, onOpen, onClose } = useDisclosure()
  const { workspaceInfo } = useContext(WorkspaceContext)
  const { name, dot, type } = workspaceInfo

  const { showToast } = Toast()

  const updateWorkspace = useMutation((params: any) => workspaceService.updateWorkspace(params))

  const handleSubmit = async (event: any) => {
    const params: IWorkspace = {
      ...event,
      id: workspaceInfo.id,
      type: event.type.value
    }

    updateWorkspace.mutate(params, {
      onSuccess: () => {
        onClose()
        queryClient.invalidateQueries('WorkspaceProfile')
      },
      onError: () => {
        showToast({
          title: constant.UNABLE_TO_UPDATE_WORKSPACE,
          status: 'error'
        })
      }
    })
  }

  return (
    <>
      <Section
        sectionTitle={'Workspace details'}
        multipleFields={[
          { fieldName: 'Workspace name:', value: name },
          { fieldName: 'Workspace type:', value: type },
          { fieldName: 'DOT #:', value: dot }
        ]}
        buttonName={'Edit details'}
        onClick={() => onOpen()}
      />

      <FormModal
        isOpen={isOpen}
        onClose={() => onClose()}
        title={'Change workspace info'}
        size={'lg'}
        formSubmitHandler={handleSubmit}
        isLoading={updateWorkspace.isLoading}
        initialValues={{
          dot: dot,
          type: { label: capitalizeFirstLetter(type), value: type },
          name: name
        }}
        modalItems={[
          {
            name: 'name',
            type: 'input',
            placeHolder: 'Enter you name',
            labelText: 'Workspace display name'
          },
          { name: 'dot', type: 'input', placeHolder: 'Enter you DOT #', labelText: 'DOT #' },
          {
            name: 'type',
            type: 'select',
            placeHolder: type,
            labelText: 'Workspace type',
            selectOptions: [
              { label: 'Carrier', value: 'carrier' },
              { label: 'Broker', value: 'broker' },
              { label: 'Shipper', value: 'shipper' }
            ]
          }
        ]}
      />
    </>
  )
}

工作空间服务.ts

import { BaseAPI } from '../../_shared/infra/services/BaseAPI'
import { ITokenService, IWorkspace } from '../../_shared/refactoredInterfaces'
import { TeamResponseDTO, UpdateWorkspaceResponseDTO, UploadWorkspaceLogoResponseDTO } from './WorkspaceDTO'

export interface IWorkspaceService {
  getWorkspace(): Promise<TeamResponseDTO>
  updateWorkspace(params: IWorkspace): Promise<UpdateWorkspaceResponseDTO>
  uploadWorkspaceLogo(params: any): Promise<UploadWorkspaceLogoResponseDTO>
}

export class WorkspaceService extends BaseAPI implements IWorkspaceService {
  constructor(tokenService: ITokenService) {
    super(tokenService)
  }

  async getWorkspace(): Promise<TeamResponseDTO> {
    const body = {
      url: '/users/account'
    }

    const { data } = await this.get(body)
    return {
      team: data?.data,
      message: data?.message
    }
  }

  async updateWorkspace(params: IWorkspace): Promise<UpdateWorkspaceResponseDTO> {
    const body = {
      url: '/accounts',
      data: params
    }
    const { data } = await this.put(body)
    return {
      message: data.message
    }
  }

  async uploadWorkspaceLogo(params: FormData): Promise<UploadWorkspaceLogoResponseDTO> {
    const body = {
      url: '/accounts/logos',
      data: params,
      headers: {
        'Content-Type': 'multipart/form-data'
      }
    }
    const response = await this.post(body)

    return response
  }
}

我也尝试了@TkDodo here提出的解决方案,但对我来说不起作用。这个解决方案将是我的救命稻草,所以提前感谢你们。

voj3qocg

voj3qocg1#

这对我很有效

jest.mock('react-query', () => ({
           useQuery: jest.fn().mockReturnValue(({ data: {...MockData}, isLoading: false,error:{} }))
         }));
vaqhlq81

vaqhlq812#

我没有足够的声誉来回复@TkDodo上面的评论(我想,这是大多数人潜伏在SO上的“代价”😅),但我 * 已经 * 发现,模拟react-queryuseQuery()返回值对于练习特定的渲染结果绝对有用,而不必费心进行到处都是await waitFor()的异步测试。
遗憾的是,我还没有找到一个现成的软件包来为我管理这个问题。
也就是说,我 * 可以 * 报告说,我已经发现了为我的单元测试创建一些帮助函数的价值,这些帮助函数生成了一个带有本文所述的反结构化键的对象。
函数签名如下所示:

export function generateUseQueryReturnValueError(error, overrides = {}) {
  // use the error argument as the reported error object and make sure to
  // set flags like `isSuccess`, `isError`, `isLoading` and the rest...
}

export function generateUseQueryReturnValueSuccess(data, overrides = {}) {
  // use the data argument as you'd expect and, obviously, set the flags
}

export function generateUseQueryReturnValueLoading(overrides = {}) {
  // same note about setting the flags correctly
}

然而,毫无价值的是,尽管这些函数做了 * 一些 *(非常轻微!)“健全”检查/预防,比如确保isError字段不能被覆盖为generateUseQueryReturnValueError()false,但显然有足够的空间来处理“真实的生活”中“不可能”的结果。

db2dz4w8

db2dz4w83#

是的,有一种方法可以通过拦截端点来模拟useQuery数据。您可以遵循以下示例:https://github.com/MitchelSt/react-course/tree/testing/rtl/react-query
或者这个视频:https://www.youtube.com/watch?v=rGK2KiP9a5Y

7dl7o3gd

7dl7o3gd4#

如果你想模拟一个返回useQuery的方法,你应该做以下事情:例如,我们有一个钩子“useExample”返回一个useQuery:

export const useExample = (props?: ResponseType): UseQueryResult<ResponseModel> =>  
 useQuery<ResponseModel>(
     "get-example",
     () => getExample(),
     {
       ...props,
       select: (data) => data,
     }   );

模拟:

const useExampleMock = (params: UseQueryResult<ResponseModel>) =>
  jest.spyOn(useConfigModule, 'useExample').mockImplementation(() => {
    return params;
  });

使用这个模拟:

export const MOCK_USE_QUERY_RESULT = {
  data: undefined,
  dataUpdatedAt: 0,
  error: null,
  errorUpdatedAt: 0,
  failureCount: 0,
  isError: false,
  isFetched: false,
  isFetchedAfterMount: false,
  isFetching: false,
  isIdle: false,
  isLoading: false,
  isLoadingError: false,
  isPlaceholderData: false,
  isPreviousData: false,
  isRefetchError: false,
  isRefetching: false,
  isStale: false,
  isSuccess: true,
  status: ‘success’,
  refetch: jest.fn(),
  remove: jest.fn(),
};

    useConfigMock({...MOCK_USE_QUERY_RESULT, isError: false, isSuccess: false});
flvlnr44

flvlnr445#

我并不总是想拦截端点,我同意在大多数情况下这是最好的方法。在这个例子中,以及我发现的其他例子中,API服务+查询/突变在负载序列化/聚合和实际网络请求之间创建了一个关注点分离,* 在我的用例中 * 我确实想进行单元测试和识别。我想AssertUI部件正在使用正确的有效负载调用变体,而不是API服务部件为了后端需要而聚合/修改该有效负载。
我只是遵循了TkDodo's series的建议,从单个模块中导出了所有查询和变体,然后使用Jest进行模拟和Assert就非常简单了

const mockOnCreateRule = jest
  .fn()
  .mockResolvedValue({ data: "mocked response" });

// this mock can be improved depending on the consumer component
jest.mock("features/rules/hooks/rule-versions.hooks", () => ({
  useCreateRuleVersion: () => ({
    mutateAsync: mockOnCreateRule,
  }),
}));

而对于Assert变异负载就像

await waitFor(() =>
      expect(mockOnCreateRule).toHaveBeenCalledWith(
        expect.objectContaining({
          parameters: expect.arrayContaining([
            { key_name: "thickness", value: "100" },
          ]),
        }),
        // Add anything for the mutation options, or provide them if necessary
        expect.anything()
      )
    );
n3schb8v

n3schb8v6#

这是我使用jest mock文档得到的结果:

import * as reactQuery from 'react-query'
...  
reactQuery.useQuery = jest.fn().mockReturnValue({ data: { ..your data.. }, isSuccess: true, ..anything else you need from useQuery.. })

这对我很有效,模拟模块和实现没有帮助

相关问题