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