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