1

I'm trying to figure out the best way to mock different return values for a custom hook that's being utilized by one of my components. Not my component/hook but to illustrate:

/hooks/useCount.js

export function useCount() {
  // does something to determine the count
  return { count }
}

/components/ListItems.js

export function ListItems(props) {
  const { count } = useCount()

  const visibleItems = props.items.slice(0, count)

  return (
    <div>
      {visibleItems.map((item) => (
        <li>item</li>
      ))}
    </div>
  )
}

/components/tests/ListItems.js

import { useCount } from '../../hooks/useCount'
import { ListItems } from '../ListItems'

const fruits = [
  'Apple',
  'Banana',
  'Cherry',
  'Dragonfruit',
  'Elderberry',
  'Fig',
  'Grape',
  'Huckleberry',
  'Icaco',
  'Jujube',
]

// I want to provide a default return value because most
// test cases will account for all list items being rendered
const mockCount = useCount.jest
  .fn()
  .mockReturnValue({ count: fruits.length + 1 })

describe(ListItems, () => {
  test('renders all items if count is greater than number of items', () => {
    const { getByText } = render(<ListItems items={fruits} />)

    expect(getByText(fruits[0])).toBeInTheDocument()
    expect(getByText(fruits[1])).toBeInTheDocument()
    expect(getByText(fruits[2])).toBeInTheDocument()
    expect(getByText(fruits[3])).toBeInTheDocument()
    expect(getByText(fruits[4])).toBeInTheDocument()
    expect(getByText(fruits[5])).toBeInTheDocument()
    expect(getByText(fruits[6])).toBeInTheDocument()
    expect(getByText(fruits[7])).toBeInTheDocument()
    expect(getByText(fruits[8])).toBeInTheDocument()
    expect(getByText(fruits[9])).toBeInTheDocument()
  })

  test('renders the correct items if count is less than the number of items', () => {
    useCount.mockReturnValueOnce({ count: 5 }) // I also want to specify a unique "count"s like this for some test cases
    const { getByText } = render(<ListItems items={fruits} />)

    expect(getByText(fruits[0])).toBeInTheDocument()
    expect(getByText(fruits[1])).toBeInTheDocument()
    expect(getByText(fruits[2])).toBeInTheDocument()
    expect(getByText(fruits[3])).toBeInTheDocument()
    expect(getByText(fruits[4])).toBeInTheDocument()
    expect(getByText(fruits[5])).not.toBeInTheDocument()
    expect(getByText(fruits[6])).not.toBeInTheDocument()
    expect(getByText(fruits[7])).not.toBeInTheDocument()
    expect(getByText(fruits[8])).not.toBeInTheDocument()
    expect(getByText(fruits[9])).not.toBeInTheDocument()
  })
})

Is this even the right approach? Should I be using a mocked implementation? Or spyOn somehow? I'm also having issues with the mockReturnValueOnce() function and Flow, so if that can be taken into account with a solution I'd be even more grateful.

warrenbuffering
  • 242
  • 4
  • 24
  • Mock custom hooks are not recommended. Custom hooks encapsulate data and operations. It is recommended to render data, trigger operations, and assert the rendering results of components. Please provide the complete code of `useCount` hook – Lin Du Oct 26 '21 at 07:11
  • 1
    Can't share the actual code, `useCount` is a stand-in for example's sake. I don't understand why its return value shouldn't be mocked when the `useCount` hook has already been tested itself. If anything I'd think this is a better practice since `useCount`'s functionality isn't being tested in multiple places and `ListItems` tests aren't being muddied by the hook. – warrenbuffering Oct 26 '21 at 07:18
  • 1
    Try add `jest.mock('../../hooks/useCount')` in the module scope of the test file – Lin Du Oct 26 '21 at 07:21

0 Answers0