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

Rah*_*hul 27 unit-testing reactjs react-testing-library react-query

我正在使用反应测试库处理测试用例。为了编写测试用例,我需要模拟 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()
})
Run Code Online (Sandbox Code Playgroud)

工作空间详细信息部分.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' }
            ]
          }
        ]}
      />
    </>
  )
}
Run Code Online (Sandbox Code Playgroud)

工作区服务.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
  }
}
Run Code Online (Sandbox Code Playgroud)

还尝试了@TkDodo在这里提出的解决方案,但对我不起作用。这个解决方案将是我的救星,所以提前感谢你们。

小智 12

这对我有用

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


小智 11

我发现有效的方法是监视react-query模块并模拟useQuery的实现。

import * as ReactQuery from 'react-query'
    
    jest
      .spyOn(ReactQuery, 'useQuery')
      .mockImplementation(
        jest
          .fn()
          .mockReturnValue({ data: { ...MockData }, isLoading: false, isSuccess: true })
      )
Run Code Online (Sandbox Code Playgroud)


小智 4

我没有足够的声誉来回复@TkDodo上面的评论(我想,主要潜伏在SO上的“价格”),但我发现mockingreact-queryuseQuery()返回值对于练习特定的渲染结果绝对有用,而无需不用担心到处都是await waitFor()s 的异步测试。

遗憾的是,我无法找到一个现成的软件包来为我管理这个问题。

也就是说,我可以报告说,我发现为我的单元测试创​​建一些辅助函数是有价值的,这些函数生成一个带有此处记录的解构键的对象。

函数签名如下:

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
}
Run Code Online (Sandbox Code Playgroud)

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