jest testing tricks with react and mobx
First of all, i use a lot of jest with @testing-library/react. I think everyone heard about it and is already using it, if not, please give it a try :)
To get everything up and testing, we need to glue everything together with the jest config and the global test setup
setup hints
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
setupFilesAfterEnv: ['./globalTestSetup.js']
};
// globalTestSetup.js
require('jest-styled-components')
require('@testing-library/jest-dom/extend-expect')
require('mobx-react-lite/batchingForReactDom')
mocking a mobx model from useContext
In my current default setup, i’m using mob-react-lite and the useContext hook to access the data store.
One chance to test it, is to write container components that are just the observers and set the mobx models as props. This is working fine, but in some cases, you might just want to mock a model and to test some behaviour
note: only variable names with “mock” prefix are usable with jest.mocks
-> jest docs
const mockFn = jest.fn()
jest.mock('../../store/MyMobxModel.ts', () => {
// return a constructor
return jest.fn().mockImplementation(() => {
// with a mock implementation
return {
data: ['overwritten initial value'],
functionToTest(input: string): string {
mockFn(input)
return '???'
},
}
})
})
beforeEach(() => {
mockFn.mockReset()
})
testing a file upload
This is just a little utility script that simulates a file upload with react-testing-library
const simulateFileUpload = (inputEl: HTMLElement) => {
const file = new File(['(⌐□_□)'], 'chucknorris.png', {
type: 'image/png',
})
act(() => {
Object.defineProperty(inputEl, 'files', {
value: [file],
})
fireEvent.change(inputEl)
})
}
and the a test using it
test('that file uploads are working', () => {)
const mock = jest.fn()
const { getByTestId } = render(<UploadComponent successCallback={mock} />)
simulateFileUpload(getByTestId('fileuploadinput'))
expect(mock).toBeCalledWith('chucknorris.png')
})
testing react router, with react router hooks
perhaps you noted some errors when testing components that needs to access useParams or some other hooks from react router. To get this test up and running, you need to provide some router context, and the easiest way is to use the MemoryRouter
import { MemoryRouter } from 'react-router-dom'
const { getByTestId } = render(<ComponentWithRoutingElements />, {
wrapper: MemoryRouter,
})
or if some specific route params are needed, you can render it directly with initial entries and a matching route to provide it
const { getByTestId } = render(
<MemoryRouter initialEntries={[`/route/Awesome`]} >
<Route path="/route/:param" component={ComponentWithUseParamsHook} />
</MemoryRouter>,
)
expect(getByTestId('headline')).toMatchSnapshot()
tests with a styled-components theme
the obvious solution ist just to wrap every test in a theme provider…
<ThemeProvider theme={Theme}>
<ComponentUnderTest />
</ThemeProvider>
One alternative i used in small atom components, is just to set the Theme as default prop. The tests are just running, and everyone can use the atoms wihtout thinking about to wrap the test
MyStyledButton.defaultProps = {
theme: Theme,
}