I’m writing component test code and I don’t know where to ask, so I’m asking a question.
I’m using @testing-libray/react-native and jest.
When I pressed the background for the first render.
I wrote some code to test when the photo button is pressed and when the camera button is pressed.
I cannot confirm whether it is possible to write the code below or if there is a wrong way to write it, so I am asking using one specific component as an example.
Is it correct to write the code below? Or is there a better direction?
When testing with the current code,
Warning: An update to Animated(View) inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
/* assert on the output */
A warning text appears.
I need help.
import { View, StyleSheet, Text, TouchableOpacity } from 'react-native';
import Modal from 'react-native-modal';
import * as ImagePicker from 'expo-image-picker';
type ImagePickerComponentProps = {
visible: boolean;
onCancel: () => void;
setImage: (image: string) => void;
};
const ImagePickerComponent = ({ visible, onCancel, setImage }: ImagePickerComponentProps) => {
const { t } = useTranslation();
const pickImage = async () => {
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: false,
quality: 1,
});
if (!result.canceled && result.assets && result.assets.length > 0) {
setImage(result.assets[0].uri);
onCancel();
}
};
const takePhoto = async () => {
const result = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: false,
quality: 1,
});
if (!result.canceled && result.assets && result.assets.length > 0) {
setImage(result.assets[0].uri);
onCancel();
}
};
return (
<Modal
testID="image-picker-modal"
isVisible={visible}
onBackdropPress={onCancel}
onBackButtonPress={onCancel}
style={{ justifyContent: 'flex-end', margin: 0 }}
backdropTransitionOutTiming={0}
backdropTransitionInTiming={0}
customBackdrop={<TouchableOpacity testID="backdrop" onPress={onCancel} style={{ flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.5)' }} />}
>
<View testID="image-picker-modal-content" style={styles.modalContent}>
<TouchableOpacity testID="photo-button" onPress={pickImage} style={styles.modalButton}>
<View
style={[
styles.modalImage,
{
backgroundColor: lightTheme.skyBlue,
},
]}
>
<Entypo name="image" size={scale(20)} color="white" />
</View>
<Text style={styles.modalText}>{t('account.photo')}</Text>
</TouchableOpacity>
<TouchableOpacity testID="camera-button" onPress={takePhoto} style={styles.modalButton}>
<View
style={[
styles.modalImage,
{
backgroundColor: lightTheme.emerald,
},
]}
>
<Entypo name="camera" size={scale(20)} color="white" />
</View>
<Text style={styles.modalText}>{t('account.camera')}</Text>
</TouchableOpacity>
</View>
</Modal>
);
};
export default ImagePickerComponent;
import React from 'react';
import * as ImagePicker from 'expo-image-picker';
import { fireEvent, render, waitFor } from '@testing-library/react-native';
import ImagePickerComponent from '../ImagePickerComponent';
describe('ImagePickerComponent', () => {
const onCancel = jest.fn();
const setImage = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
it('visible이 true일 때 올바르게 렌더링된다', () => {
const { getByTestId } = render(<ImagePickerComponent visible onCancel={onCancel} setImage={setImage} />);
expect(getByTestId('image-picker-modal')).toBeTruthy();
expect(getByTestId('photo-button')).toBeTruthy();
expect(getByTestId('camera-button')).toBeTruthy();
});
it('백드롭을 눌렀을 때 onCancel이 호출된다', async () => {
const { getByTestId, rerender } = render(<ImagePickerComponent visible onCancel={onCancel} setImage={setImage} />);
fireEvent.press(getByTestId('backdrop'));
await waitFor(() => {
expect(onCancel).toHaveBeenCalled();
});
await waitFor(
() => {
rerender(<ImagePickerComponent visible={false} onCancel={onCancel} setImage={setImage} />);
},
);
expect(getByTestId('image-picker-modal')).toBeVisible();
});
it('사진 버튼을 눌렀을 때 사진 선택 모달이 나타나고 사진이 선택되면 setImage가 호출된다', async () => {
jest.spyOn(ImagePicker, 'launchImageLibraryAsync').mockResolvedValue({
canceled: false,
assets: [{ uri: 'test-uri', width: 100, height: 100 }],
});
const { getByTestId } = render(<ImagePickerComponent visible onCancel={onCancel} setImage={setImage} />);
await waitFor(async () => {
fireEvent.press(getByTestId('photo-button'));
});
await waitFor(() => {
expect(ImagePicker.launchImageLibraryAsync).toHaveBeenCalledWith({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: false,
quality: 1,
});
});
expect(setImage).toHaveBeenCalledWith('test-uri');
expect(onCancel).toHaveBeenCalled();
});
it('카메라 버튼을 눌렀을 때 카메라 모달이 나타나고 사진이 촬영되면 setImage가 호출된다', async () => {
jest.spyOn(ImagePicker, 'launchCameraAsync').mockResolvedValue({
canceled: false,
assets: [{ uri: 'test-uri', width: 100, height: 100 }],
});
const { getByTestId } = render(<ImagePickerComponent visible onCancel={onCancel} setImage={setImage} />);
await waitFor(async () => {
fireEvent.press(getByTestId('camera-button'));
});
await waitFor(() => {
expect(ImagePicker.launchCameraAsync).toHaveBeenCalled();
});
expect(setImage).toHaveBeenCalledWith('test-uri');
expect(onCancel).toHaveBeenCalled();
});
});