Can someone guide me on how to properly mock an axios post request? I've been struggling with the documentation and various solutions from stack overflow. Each attempt either results in typescript errors or the undesired outcomes depicted below...
https://i.sstatic.net/HMRwn.png
Here is the component being rendered:
import React, { useState } from 'react';
import { Image, StyleSheet, Text, TextInput, View } from 'react-native';
import { Formik } from 'formik';
import * as Yup from 'yup';
import { MaterialCommunityIcons as Icon } from '@expo/vector-icons';
import { BtnMain, MainView } from '@app/components';
import { useAuthStore } from '@app/stores';
import { apiGetCurrentUser, apiLogin } from '@app/apis';
const validationSchema = Yup.object({
username: Yup.string().required('Username required'),
password: Yup.string().required('Password required')
});
export default function ScreenLogin(): JSX.Element {
const [isLoggingIn, setIsLoggingIn] = useState(false);
const [hidePassword, setHidePassword] = useState(true);
const { setIsViewerAuthenticated, setViewerInfo } = useAuthStore(store => store);
const loginHander = async (values: { username: string; password: string }): Promise<void> => {
try {
setIsLoggingIn(true);
const responseToken = await apiLogin(values.username, values.password);
if (!responseToken) {
throw new Error('Access Denied');
}
await setIsViewerAuthenticated(responseToken);
const responseViewerInfo = await apiGetCurrentUser();
await setViewerInfo(responseViewerInfo);
} catch (error: any) {
throw error;
} finally {
setIsLoggingIn(false);
}
};
return (
<MainView>
<Formik
initialValues={{
username: '',
password: '',
submitError: null
}}
validationSchema={validationSchema}
onSubmit={(values, { setErrors }) =>
loginHander(values).catch(error => setErrors({ submitError: error.message }))
}
>
{({
handleChange,
handleBlur,
handleSubmit,
values,
errors
// isValid, dirty
}) => (
<View style={styles.container}>
<Image source={require('@/assets/images/vlogo.png')} style={styles.image} />
<View style={styles.form}>
<View>
<TextInput
style={styles.inputMain}
placeholder="Username"
onBlur={handleBlur('username')}
onChangeText={handleChange('username')}
value={values.username}
/>
{errors.username && <Text style={styles.error}>{errors.username}</Text>}
</View>
<View>
<View style={styles.inputContainer}>
<TextInput
style={styles.inputPswd}
placeholder="Password"
secureTextEntry={hidePassword}
onBlur={handleBlur('password')}
onChangeText={handleChange('password')}
value={values.password}
/>
<Icon
style={styles.eyeIcon}
onPress={() => setHidePassword(!hidePassword)}
name={hidePassword ? 'eye-off' : 'eye'}
size={20}
/>
</View>
{errors.password && <Text style={styles.error}>{errors.password}</Text>}
</View>
<View>
<BtnMain
btnName="Login"
isLoading={isLoggingIn}
btnStyles={styles.btn}
btnTextStyles={styles.txtLogin}
onPress={handleSubmit}
/>
{errors.submitError && <Text style={styles.submitError}>{errors.submitError}</Text>}
</View>
</View>
</View>
)}
</Formik>
</MainView>
);
}
This depicts the spyon function:
export async function apiLogin(username: string, password: string): Promise<string> {
try {
const result = await axiosLoginRequest([mapApiEndpoints.login, { username: username, password: password }]);
return result.data.Response;
} catch (error: any) {
throw new Error(error);
}
}
Attempted test #1 using axios mocking:
import React from 'react';
import { render, fireEvent, waitFor, cleanup, screen } from '@testing-library/react-native';
import ScreenLogin from './ScreenLogin';
jest.mock('expo-asset');
jest.mock('expo-font');
const mockAxios = {
post: jest.fn(() => Promise.resolve({ data: {} }))
};
describe('Testing Login screen...', () => {
afterAll(() => {
cleanup();
});
it('renders inputs and button', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
expect(userInput).toBeTruthy();
expect(passwordInput).toBeTruthy();
expect(loginButton).toBeDefined();
});
it('enter username/password and click login', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
await waitFor(() => fireEvent.changeText(userInput as never, 'test1'));
expect(userInput.props.value).toEqual('test1');
await waitFor(() => fireEvent.changeText(passwordInput as never, 'pass1'));
expect(passwordInput.props.value).toEqual('pass1');
expect(loginButton).toBeDefined();
mockAxios.post.mockImplementationOnce(() =>
Promise.resolve({
data: {
results: 'token'
}
})
);
await waitFor(() => fireEvent(loginButton, 'click'));
expect(mockAxios.post).toHaveBeenCalledTimes(1);
});
});
Attempted test #2 using spyOn:
import React from 'react';
import { render, fireEvent, waitFor, cleanup, screen } from '@testing-library/react-native';
import ScreenLogin from './ScreenLogin';
import { apiLogin } from '@app/apis/index';
const api = { apiLogin };
jest.mock('expo-asset');
jest.mock('expo-font');
jest.spyOn(api, 'apiLogin').mockResolvedValue('token');
describe('Testing Login screen...', () => {
afterAll(() => {
cleanup();
jest.resetAllMocks();
});
it('renders inputs and button', async () => {
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
expect(userInput).toBeTruthy();
expect(passwordInput).toBeTruthy();
expect(loginButton).toBeDefined();
});
it('enter username/password and click login', async () => {
// setup
render(<ScreenLogin />);
const userInput = screen.getByPlaceholderText('Username');
const passwordInput = screen.getByPlaceholderText('Password');
const loginButton = screen.getByText('Login');
// enter credentials
await waitFor(() => fireEvent.changeText(userInput as never, 'test1'));
expect(userInput.props.value).toEqual('test1');
await waitFor(() => fireEvent.changeText(passwordInput as never, 'pass1'));
expect(passwordInput.props.value).toEqual('pass1');
// login
await waitFor(() => fireEvent.press(loginButton));
// results
expect(api.apiLogin).toHaveBeenCalledTimes(1);
});
});
If anyone can offer assistance, it would be greatly appreciated.