How to create a snapshot?
expect( render(<Component />).toJSON() ).toMatchSnapshot();
How to find an element and press it?
You can use queryByxxx or getByxxx. The difference is
– queryByxxx still returns the element as null if the element is not found
– getByxxx throws error if element is not found
How to find an element by testId?
const component = render(<Component />); const backButton = component.queryByTestId('id-back'); expect(backButton).toBeDefined();
How to find an element by a11y?
const component = render(<Component />); const backButton = component.queryByA11yLabel('back'); // const backButton = component.getByLabelText('back'); expect(backButton).toBeDefined();
How to find a text string inside an element?
const component = render(<Component />); const element = component.getByTestId('id-element'); expect(element.props.children).toBe('--- a string ---');
How to press an element?
import { fireEvent, waitFor } from '@testing-library/react-native' const component = render(<Component />) const button = component.queryByA11yLabel('back') await( waitFor( () => expect(button).toBeTruthy() ) ) fireEvent.press(button) // fireEvent(button, 'click') expect( getByTestId('id-visible-text') ).toBeTruthy();
How to create mock data?
How to create a mock component which throws error?
it('renders ErrorView when an error occurs', () => { const ThrowError = () => { throw new Error('Test error'); } const { getByTestId } = render( <ErrorBoundary> <ThrowError /> </ErrorBoundary> ); expect(getByTestId('id-error-view')).toBeCalled; });
How to mock a library?
- Library
{ useSomethings: { method1: Function; method2: Function; events: { store: { clear: Function; }; }; } }
// Example of using this library import { useSomethings } from '@me/library' const MyComponent = () => { const { method1, method2, events } = useSomethings const { store } = events const { clear } = store ... }
- Mock file
class MockEvents { public store = jest.fn().mockReturnValue( clear: jest.fn(); ); }; class MockUseSomethings { public method1 = jest.fn(); public method2 = jest.fn(); public events = new MockEvents(); } class MockMyLib { constructor() { jest.mock('@me/library', () => { return { ...jest.requireActual('@me/library'), useSomething: jest.fn().mockReturnValue(new MockUseSomethings()) } }) } } export const mocks = { myLib: new MockMyLib() }
Test file
const mockClear = jest.spyOn(mocks.myLib.useSomething.events.store(), 'clear'); expect(mockClear).toHaveBeenCalledTimes(1);
How to test a react-native hook?
Hook
const useSomething = (method1, method2) => { const navigation = useNavigation() const loginHandler = useCallback( async () => { ... navigation.navigate('login') try { const result = await method1() if (result) method2('post-login') } catch (e) { navigation.navigate('welcome') } }, [method1, method2] ) return { loginHandler } }
Test
// Mock useNavigation and useNavigation.navigate jest.mock('@react-navigation/native', () => { useNavigation: jest.fn() }) const mockUseNavigation = useNavigation as jest.Mock const mockNavigation = { navigate: jest.fn() } as any // Mock arguments const mockMethod1 = jest.fn() const mockMethod2 = jest.fn() beforeEach(() => { jest.clearAllMocks() mockUseNavigation.mockReturnValue(mockNavigation) }) it('should navigate to Login screen', async () => { expect.assertions(3) // Call hook const { result } = renderHook(() => useSomething(mockMethod1, mockMethod2)) await result.current.loginHandler() // Test argument call expect(mockMethod2).toHaveBeenCalledTimes(0) // Test useNavigation.navigate call expect(mockNavigation.navigate).toHaveBeenCalledTimes(1) expect(mockNavigation.navigate).toHaveBeenNthCalledWith(1, 'login') }) it('should navigate to Login and back to Welcome screen due to error', async () => { expect.assertions(4) // Mock reject for method1 mockMethod1.mockReturnValue(Promise.reject('error')) // Call hook const { result } = renderHook(() => useSomething(mockMethod1, mockMethod2)) await result.current.loginHandler() // Test argument call expect(mockMethod2).toHaveBeenCalledTimes(0) // Test useNavigation.navigate call expect(mockNavigation.navigate).toHaveBeenCalledTimes(2) expect(mockNavigation.navigate).toHaveBeenNthCalledWith(1, 'login') expect(mockNavigation.navigate).toHaveBeenNthCalledWith(2, 'welcome') }) it('should navigate to Login, then call post-login', async () => { expect.assertions(4) // Mock reject for method1 mockMethod1.mockReturnValue(Promise.resolve('abc')) // Call hook const { result } = renderHook(() => useSomething(mockMethod1, mockMethod2)) await result.current.loginHandler() // Test useNavigation.navigate call expect(mockNavigation.navigate).toHaveBeenCalledTimes(1) expect(mockNavigation.navigate).toHaveBeenNthCalledWith(1, 'login') // Test method2 expect(method2).toHaveBeenCalledTimes(1) expect(method2).toHaveBeenNthCalledWith(1, 'post-login') })
How to test a component having a hook/ an import component/ a custom function?
- Main component
// MyComponent.tsx import { useNavigation } from '@react-navigation/native' // A hook import { ComponentSomething } from '@me/ui-library' // An import component import { navigateToSomewhere } from './format' // A custom function const MyComponent = () => { const navigation = useNavigation() return <View testID={'id-test-my-component'}> <ComponentSomething /> <Button testID={'id-test-button'} onPress={() => navigateToSomewhere(abc)} /> </View> }
// format export const navigateToSomewhere = (abc) => { ... }
- Test file
import * as helper from './format' import MyComponent from '../MyComponent' import { fireEvent, waitFor } from '@testing-library/react-native' import { myRender } from './testHelper' // Mock a hook of react-navigation jest.mock('@react-navigation/native', () => ({ useNavigation: jest.fn(); })); /* // or export class MockReactNavigation { public useNavigation = jest.fn(); constructor() { jest.mock(('@react-navigation/native', () => ({ useNavigation: useNavigation; }))); } } */ // Mock an import UI component jest.mock(('@me/ui-library', () => ({ const View = require('react-native/Libraries/Components/View/View') return { ComponentSomething: View } }))); // Mock a custom function const mockCustomFunc = jest.spyOn(helper, 'navigateToSomewhere').mockImplementation() test('Render and fire NavigateSomewhere', async () => { const { getByTestId, queryByTestId } = myRender(<MyComponent />) const button = getByTestId('id-test-button'); await waitFor(() => expect(button).toBeTruthy()) fireEvent.press(button) expect(mockCustomFunc).toBeCalled() })
// testHelper export myRender = (ui: ReactElement, options?: ReactElement) => render( ui, { wrapper: Providers, ...options } ) const Providers = ({ children }) => { return ( <Theme ...> {children} </Theme> ) }
How to test opening a link in a browser app?
import { Linking } from 'react-native' export const openBrowserApp = async(url: string) => { const supported = await Linking.canOpenURL(url) if (supported) { Linking.openURL(url) } }
// Mock methods of an object jest.mock('react-native/Libraries/Linking/Linking', () => ({ canOpenURL: jest.fn().mockResolvedValue(true), openURL: jest.fn().mockResolvedValue(null), addEventListener: jest.fn(), removeEventListener: jest.fn() })) it('should accept an url and open it', async () => { expect(openBrowserApp('https://google.com/')).toBeTruthy() })
How to test an independent function having an argument of a method?
Main function
// Usage const MyComponent = () => { const { dispatchEvent } = useSomething(); ... return <Button onPress={() => navigateToScreen(dispatchEvent, 'screenName')} /> }
// I want to test this function export const navigateToScreen = (dispatch: DispatchAction, route: string) => { dispatch('EVENT_NAME', { route: route }) }
Test files
////////////////////// // Create mock data class MockSomething { public dispatchEvent = jest.fn() ... } class MockClass{ public useSomething = new MockSomething() constructor() { jest.mock('@me/my-lib-module', () => { return { ...jest.requireActual('@me/my-lib-module'), useSomething: jest.fn().mockReturnValue(this.useSomething) } }) } } /////////////////////////////// // Use it in your test file it('should dispatch an event', async() => { const mockClass = new MockClass() navigateToScreen(mockClass.useSomething.dispatchEvent, 'aRouteName') expect(mockClass.useSomething.dispatchEvent).toHaveBeenCalledWith('EVENT_NOW', { route: 'aRouteName' }) })
How to test a function returning an instance of a class?
A function returning apollo client instance
export const getApolloClient = (something) => { ... return new ApolloCLient({ link:... cache, }) }
Test file
import { getApolloClient } from './helper' import { ApolloClient } from '@apollo/client' it('create an apollo client', () => { const something = jest.fn() expect(getApolloClient(something)).toBeInstanceOf(ApolloClient) })
How to test a function calling child methods of a class instance?
Main function
import ParentClass 'src/methods/ParentClass'; export const myFunction (parentClassInstance: ParentClass) { parentClassInstance.methodA(); parentClassInstance.methodB(); }
class ParentClass { function methodA() { ... } function methodB() { ... } }
Test file
import { myFunction } from '..' jest.mock('src/methods/ParentClass', () => { jest.fn().mockImplementation(() => { return { methodA: jest.fn(), methodB: jest.fn() } }) }) describe('MyFunction', () => { const parentClass = new ParentClass(); myFunction(parentClass); it('should call methodA', async() => { expect(parentClass.methodA).toHaveBeenCalledTimes(1); }); it('should call methodB', async() => { expect(parentClass.methodB).toHaveBeenCalledTimes(1); }); })
Issues
How to fix “import unexpected token” on Jest?
Add .babelrc file
{ "env": { "test": { "presets": [ "@babel/preset-env", "@babel/preset-react" ] } } }
Install dependencies
yarn add -D @babel/core @babel/preset-env @babel/preset-react
Detox
How to run detox?
detox build detox test
How to execute different test file in order?
Set test file name as “01-xxxx-yyyy.spec.js”, “02-aaaaa-zzzzz.spec.js”
Leave a Reply