在某些测试中仅使用Jest模拟React组件

6

这个问题非常好:Jest每个测试用例如何mock模块

但它并没有回答我如何仅在一个单独的测试用例中mock它并保持其余测试使用原始组件。

如果我只想对快照进行一些 mocking,而其他测试不需要 mocking,那么这就有意义了。

这是我目前的尝试:

import React from 'react'
import { render } from '@testing-library/react'

jest.mock('components/Photo', () =>
  jest.fn(() => jest.requireActual('components/Photo'))
)
import Photo from 'components/Photo'

import PhotoGrid from '.'

beforeEach(() => {
  Photo.mockReset()
})

test('default renders 9 photos if provided 9', () => {
  const photos = [...Array(9).keys()]
  const { getAllByTestId, debug } = render(<PhotoGrid photos={photos} />)

  debug()
  expect(getAllByTestId(PHOTO_COMP_TEST_ID)).toHaveLength(9)
})

test('renders with masonry grid style', () => {
  Photo.mockImplementation(() => <div />)
  const photos = [...Array(9).keys()]
  const { container, debug } = render(<PhotoGrid photos={photos} />)

  debug()

  expect(container).toMatchInlineSnapshot(`
    <div>
      ... 
    </div>
  `)
})

这是需要测试的组件

import React from 'react'
import Masonry from 'react-masonry-css'
import './index.css'

import Photo from 'components/Photo'

function PhotoGrid({ photos, numberOfPhotos = 9 }) {
  const imgs = photos ? photos.slice(0, numberOfPhotos) : []

  const breakpointColumnsObj = {
    default: 4,
    1300: 3,
    900: 2,
    700: 1,
  }

  return (
    <Masonry
      breakpointCols={breakpointColumnsObj}
      className="my-masonry-grid"
      columnClassName="my-masonry-grid_column"
    >
      {imgs &&
        imgs.map(({ id, secret, server, farm }, index) => (
          <div key={index} className="masonry-item">
            <Photo id={id} secret={secret} server={server} farm={farm} />
          </div>
        ))}
    </Masonry>
  )
}

export default PhotoGrid

这个工作了吗? - La pach'
2个回答

6

在测试中,使用jest.mock(...)来对想要模拟的组件进行模拟。

使用jest.requireActual(...)实现默认行为。

使用mockImplementation来实现自定义行为。

import React from 'react'
import { render } from '@testing-library/react'

jest.mock('components/Photo')
import Photo from 'components/Photo'

import PhotoGrid from '.'

// This maintains the original implementation for tests. 
// This step is important because if not done, it will
// result in empty render errors.
beforeEach(() => {
  Photo.mockImplementation(jest.requireActual('components/Photo'));
}) 
 

// This test will use original implementation of 'components/Photo'
test('default renders 9 photos if provided 9', () => {
  const photos = [...Array(9).keys()]
  const { getAllByTestId, debug } = render(<PhotoGrid photos={photos} />)

  debug()
  expect(getAllByTestId(PHOTO_COMP_TEST_ID)).toHaveLength(9)
})

// This test will use the mocked implementation of 'components/Photo'
test('renders with masonry grid style', () => {
  Photo.mockImplementation(() => <div />)
  const photos = [...Array(9).keys()]
  const { container, debug } = render(<PhotoGrid photos={photos} />)

  debug()

  expect(container).toMatchInlineSnapshot(`
    <div>
      ... 
    </div>
  `)
})

3
当我这样做时,出现了“TypeError: specificMockImpl.apply不是一个函数”的错误。 - Kenji Miwa
这个不适用于mockImplementationOnce。 - Luis

5

我没有权限添加评论,所以我就在这里留言了。

我尝试了Han Daniels提供的解决方案,但是出现了错误:

TypeError: specificMockImpl.apply is not a function.

我通过在jest.requireActual('components/Photo')之后添加.default来修复它,因为Photo是默认导出。

所以变成:jest.requireActual('components/Photo').default


谢谢,老兄。我也遇到了同样的问题。那么,这个不带默认值的语法只适用于没有默认导出的情况吗? - Luis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接