【问题标题】:Is there any solution to mock react-query's useQuery and useMutation while working with react testing library在使用反应测试库时,是否有任何解决方案可以模拟反应查询的 useQuery 和 useMutation
【发布时间】:2022-01-10 14:50:41
【问题描述】:

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

WorkspaceDetailsS​​ection.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()
})

WorkspaceDetailsS​​ection.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' }
            ]
          }
        ]}
      />
    </>
  )
}

WorkspaceService.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 提出的解决方案,但对我不起作用。 这个解决方案对我来说是一个救命稻草,所以提前谢谢你们。

【问题讨论】:

  • 我认为告诉你模拟整个钩子不是最好的方法并且只模拟网络请求可能更好,这没有多大意义。但是您还没有说明您的代码有什么问题?当您尝试模拟网络请求时,哪些不起作用,哪些不起作用?官方文档也有一个简单的 nock 示例:react-query.tanstack.com/guides/testing#testing-network-calls

标签: reactjs unit-testing react-testing-library react-query


【解决方案1】:

我没有足够的声誉来回复@TkDodo 上面的评论(我想大部分潜伏在 SO 上的“价格”,我想 ?),但我发现嘲笑 react-queryuseQuery() 返回值对于执行特定的渲染结果绝对有用,而不必费心处理到处都是await waitFor()s 的异步测试。

遗憾的是,我还没有找到一个现成的包来为我管理这个。

也就是说,我可以报告说,我已经为我的单元测试创​​建了一些帮助函数,这些函数生成了一个对象,其中包含here 记录的解构键。 p>

函数签名如下:

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 字段不能被覆盖为false generateUseQueryReturnValueError(),“现实生活”中“不可能”的结果显然有足够的空间。

【讨论】:

    【解决方案2】:

    是的,有一种方法可以通过拦截端点来模拟 useQuery 数据。 你可以按照这个例子:https://github.com/MitchelSt/react-course/tree/testing/rtl/react-query

    或此视频:https://www.youtube.com/watch?v=rGK2KiP9a5Y

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-13
    • 2021-05-25
    • 2021-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-03
    相关资源
    最近更新 更多