You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

@testing-library/react-native中axios post模拟调用次数验证失败求助

模拟Axios POST请求的测试问题

我需要解决React Native项目中模拟Axios POST请求的测试问题,现有文档未能提供有效帮助。尝试了Stack Overflow上能找到的各种方案,但最终要么出现TypeScript错误,要么得到如下不符合预期的结果:

测试不符合预期的结果

渲染组件代码

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"
                  // isDisabled={isLoggingIn || !dirty || !isValid}
                  isLoading={isLoggingIn}
                  btnStyles={styles.btn}
                  btnTextStyles={styles.txtLogin}
                  onPress={handleSubmit}
                />
                {errors.submitError && <Text style={styles.submitError}>{errors.submitError}</Text>}
              </View>
            </View>
          </View>
        )}
      </Formik>
    </MainView>
  );
}

apiLogin函数代码

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);
  }
}

测试尝试1(Axios Mock)

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('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);
  });
});

测试尝试2(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('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);
  });
});

恳请各位提供帮助。

内容的提问来源于stack exchange,提问作者Fiddle Freak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:34:54