I need some help understanding how one can test an application using React
Context
.
Here's my sample setup.
context.js
import React from 'react' export const AppContext = React.createContext()
App.js
import React from 'react' import MyComponent from './MyComponent' import {AppContext} from './context' const App extends React.Component { state = { items: [] } handleItemAdd = newItem => { const {items} = this.state items.push(newItem) this.setState(items) } render() { return ( <AppContext.Provider value={{ addItem: this.handleItemAdd }}> <MyComponent /> </AppContext.Provider> ) } } export default App
MyComponent.js
import React from 'react' import {AppContext} from './context' const MyComponent extends React.Component { render() { return ( <AppContext.Consumer> {addItem => <button onClick={() => addItem('new item')}> Click me </button> } </AppContext.Consumer> ) } } export default MyComponent
This is a simplified example. Imagine that there are more layers between App
and MyComponent
, hence the use of React
Context
.
And here's my test file for MyComponent
.
MyComponent.test.js
import React from 'react' import {render, fireEvent} from 'react-testing-library' test('component handles button click', () => { const {getByText} = render( <MyComponent /> ) const button = getByText('Click me') fireEvent.click(button) expect...? }
The thing is, AppContext.Consumer
is part of the implementation of MyComponent
, so in this test I don't have direct access to it. How do I mock AppContext.Consumer
so I am actually able to verify that clicking a button fires a function call?
I know that in theory I can test this by rendering MyComponent
in my App
, but I want to write a unit test for MyComponent
only.
The best way to test Context is to make our tests unaware of its existence and avoiding mocks. We want to test our components in the same way that developers would use them (behavioral testing) and mimic the way they would run in our applications (integration testing).
To mock a React component, the most straightforward approach is to use the jest. mock function. You mock the file that exports the component and replace it with a custom implementation. Since a component is basically a function, the mock should also return a function.
A simple example: import React, { useContext } from 'react'; const MyComponent = () => { const name = useContext(NameContext); return <div>{name}</div>; }; When testing this component with the shallow renderer from react and jest snapshots.
You just render the context with your component.
const addItem = jest.fn() render( <AppContext.Provider value={{ addItem }}> <MyComponent /> </AppContext.Provider> )
See Mocking context with react-testing-library
I want to add a complete test example by using the solution from @Giorgio. Here we are testing that MyComponent is rendered and that its button will be clicked once.
MyComponent.test.js
import React from 'react' import { render, fireEvent } from 'react-testing-library' test('component handles button click', () => { const addItem = jest.fn() render( <AppContext.Provider value={{ addItem }}> <MyComponent /> </AppContext.Provider> ) fireEvent.click(screen.getByText(/click me/)) expect(addItem).toHaveBeenCalledTimes(1) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With