Skip to content

Custom Hooks with useContext #283

Closed
Closed
@ajlende

Description

@ajlende

First, I'd like to thank you for getting react-testing-library updated so quickly after the release of hooks!

Describe the feature you'd like:

I'd like to add an option to testHook() for testing custom hooks that use useContext() and need to be wrapped in the Provider.

Below is a minimal (albeit silly) example of how useContext() could be used in a custom hook. I've used useContext() in a similar manner to this in more complicated custom hooks.

// examples/react-context-hook.js
import React from 'react'

const NameContext = React.createContext('Unknown')

function useGreeting() {
  const name = useContext(NameContext)
  return `Hello, ${name}!`
}

export {NameContext, useGreeting}

Suggested implementation:

[Option 1] Providing the Context

// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'

import {NameContext, useGreeting} from '../react-context-hook'

afterEach(cleanup)

test('provides the default value from context', () => {
  let name
  testHook(() => (name = useGreeting()), NameContext)
  expect(name).toBe('Hello, Unknown!')
})

test('provides the custom value from context', () => {
  let name
  testHook(() => (name = useGreeting()), NameContext, 'CustomName')
  expect(name).toBe('Hello, CustomName!')
})

[Option 2] Providing a Fixture

// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'

import {NameContext, useGreeting} from '../react-context-hook'

afterEach(cleanup)

test('provides the value from context', () => {
  const Fixture = ({ children }) => (
    <NameContext.Provider>
      {children}
    </NameContext.Provider>
  )
  let name
  testHook(() => (name = useGreeting()), Fixture)
  expect(name).toBe('Hello, Unknown!')
})

test('provides the custom value from context', () => {
  const Fixture = ({ children }) => (
    <NameContext.Provider value="CustomName">
      {children}
    </NameContext.Provider>
  )
  let name
  testHook(() => (name = useGreeting()), Fixture)
  expect(name).toBe('Hello, CustomName!')
})

[Option 3] Providing a Component and Props

// examples/__tests__/react-context-hook.js
import {testHook, cleanup} from 'react-testing-library'

import {NameContext, useGreeting} from '../react-context-hook'

afterEach(cleanup)

test('provides the default value from context', () => {
  let name
  testHook(() => (name = useGreeting()), NameContext.Provider)
  expect(name).toBe('Hello, Unknown!')
})

test('provides the custom value from context', () => {
  let name
  testHook(() => (name = useGreeting()), NameContext.Provider, { value: 'CustomName' })
  expect(name).toBe('Hello, CustomName!')
})

[Option 4] Some variation on the options above

I like the conciseness of 1 and 3, but 2 seems like it would probably be the most versatile. These were just my initial thoughts, so there's probably something that will work better than any of these.

Describe alternatives you've considered:

I'm doing something similar to this right now, but I could probably update my helper to work more like how testHook is implemented.

import {render, cleanup} from 'react-testing-library'

import {NameContext, useGreeting} from '../react-context-hook'

function renderWithContext(node, {value, ...options}) {
  return render(
    <NameContext.Provider value={value}>
      {node}
    </NameContext.Provider>,
    options
  )
}

afterEach(cleanup)

test('provides the custom value from context', () => {
  function Fixture() {
    const greeting = useGreeting()
    return `${greeting}`
  }
  const { queryByText } = renderWithContext(<Fixture />)
  expect(queryByText('Hello, Unknown!')).toBeTruthy()
})

test('provides the custom value from context', () => {
  function Fixture() {
    const greeting = useGreeting()
    return `${greeting}`
  }
  const { queryByText } = renderWithContext(<Fixture />, { value: 'CustomName' })
  expect(queryByText('Hello, CustomName!')).toBeTruthy()
})

Teachability, Documentation, Adoption, Migration Strategy:

Examples from above can be used. Adding the additional argument would be a minor release probably, so existing tests wouldn't need to be updated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions