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

React导出箭头函数用于Jest单元测试遇checkIfBlank未定义问题

解决Jest测试中React组件内部函数未定义的问题

问题背景

在对Register组件进行Jest单元测试时,始终遇到checkIfBlank函数未定义的错误。该函数原本是组件内部的箭头函数,直接尝试导出内部函数无法生效——因为它处于组件函数的闭包范围内,外部无法访问。

解决方案

核心是将checkIfBlank重构为纯函数,脱离组件内部的state依赖,改为通过参数接收所需数据和方法,再导出该函数供测试调用,同时在组件内调用时传入对应参数。

步骤1:重构并导出checkIfBlank

将checkIfBlank移到Register组件外部,改为接收参数的纯函数,并添加导出语句:

import React from "react";
import axios from 'axios';
import bycrypt from 'bcryptjs';
import { Link } from "react-router-dom";
import { Helmet } from "react-helmet";
import { useState } from "react";
import InputBoxForInfo from "../components/input-box-for-info";
import Button from "../components/button";
import "./register.css";

// 移到组件外部,作为独立纯函数并导出
export const checkIfBlank = (name, surname, email, username, password, setErrorMessage, checkIfUserExists) => {
  const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const passwordPattern = /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).{8,}$/;

  // 字段为空时提示
  if (!name || !surname || !email || !username || !password) {
    alert("Please enter all details");
    return;
  }

  // 验证邮箱和密码格式
  if (emailPattern.test(email) && passwordPattern.test(password)) {
    checkIfUserExists();
  } else {
    if (!emailPattern.test(email)) {
      setErrorMessage('Please enter a valid email');
    }
    if (!passwordPattern.test(password)) {
      setErrorMessage("Please enter a stronger password");
    }
  }
};

const Register = (props) => {
  const [name, setName] = useState("");
  const [surname, setSurname] = useState("");
  const [email, setEmail] = useState("");
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const salt = bycrypt.genSaltSync(10);
  const hashedPassword = bycrypt.hashSync(password,salt);
  const[exist, setExistence] = useState("");
  const [errorMessage, setErrorMessage] = useState('');

  const postDetails = () =>{
    axios.post("http://localhost:3002/api/post/register",{name:name, surname:surname, email: email, username: username, password: hashedPassword});
    setTimeout(function(){
    window.location.href = 'http://localhost:3000/login';
    }, 1000);
  }

  const checkIfUserExists = () =>{
    axios
    .get("http://localhost:3002/api/get/doesExist/" + username)
    .then(function(response){
      const userExists = response.data;
      console.log(response.data);
      if (JSON.stringify(userExists) == "[]"){
        setErrorMessage('Account created successfully');
        postDetails();
      }
      else{
        setErrorMessage('Username already exists');
      }
    });
  }

  const handleRegister = () => {
    console.log(
      `Name: ${name}, Surname: ${surname} Email: ${email}, Username: ${username}, Password: ${hashedPassword}`
    );
    // 调用重构后的函数,传入所需参数
    checkIfBlank(name, surname, email, username, password, setErrorMessage, checkIfUserExists);
  };

  // 组件return部分保持不变
  return (
    <div className="register-container">
      <Helmet>
        <title>register - Project ARENA</title>
        <meta property="og:title" content="register - Project ARENA" />
      </Helmet>
      <div className="register-container1">
        <Link to="/" className="register-navlink">
          <svg
            viewBox="0 0 877.7142857142857 1024"
            className="register-backbtn"
          >
            <path d="M519.429 797.143l58.286-58.286c14.286-14.286 14.286-37.143 0-51.429l-175.429-175.429 175.429-175.429c14.286-14.286 14.286-37.143 0-51.429l-58.286-58.286c-14.286-14.286-37.143-14.286-51.429 0l-259.429 259.429c-14.286 14.286-14.286 37.143 0 51.429l259.429 259.429c14.286 14.286 37.143 14.286 51.429 0zM877.714 512c0 242.286-196.571 438.857-438.857 438.857s-438.857-196.571-438.857-438.857 196.571-438.857 438.857-438.857 438.857 196.571 438.857 438.857z"></path>
          </svg>
        </Link>
      </div>
      <div className="register-container5">
        <span className="register-text">
          <span>Register</span>
          <br></br>
        </span>

        <InputBoxForInfo
          buttonText="NAME"
          onChange={(e) => setName(e.target.value)}
          rootClassName="input-box-for-info-root-class-name"
        ></InputBoxForInfo>

        <InputBoxForInfo
          buttonText="SURNAME"
          onChange={(e) => setSurname(e.target.value)}
          rootClassName="input-box-for-info-root-class-name2"
        ></InputBoxForInfo>
    
        <InputBoxForInfo
          buttonText="EMAIL"
          onChange={(e) => setEmail(e.target.value)}
          rootClassName="input-box-for-info-root-class-name3"
        ></InputBoxForInfo>
    
        <InputBoxForInfo
          buttonText="USERNAME"
          onChange={(e) => setUsername(e.target.value)}
          rootClassName="input-box-for-info-root-class-name4"
        ></InputBoxForInfo>
    
        <InputBoxForInfo
          buttonText="PASSWORD"
          onChange={(e) => setPassword(e.target.value)}
          isPassword
          rootClassName="input-box-for-info-root-class-name5"
        ></InputBoxForInfo>
    
        <br></br>
        <Button 
          name="Register"
          onClick={() => {
            console.log("Register button clicked");
            handleRegister();
          }}
          rootClassName="button-root-class-name"
        ></Button>
        
        <br></br>
        {errorMessage && <div className="error">{errorMessage}</div>}
        <span className="register-text3">Already have an account?</span>
        <Link to="/login" className="register-navlink1 button">
          Login here
        </Link>
      </div>
    </div>
  );
};

export default Register;

步骤2:调整Jest测试代码

现在checkIfBlank是纯函数,测试时需要传入模拟的checkIfUserExists方法(原测试遗漏了该参数),同时模拟alert方法:

import React from 'react';
import Register from './register';
import { checkIfBlank } from './register.js';

// 测试套件
describe('checkIfBlank', () => {
  let setErrorMessage = jest.fn();
  let checkIfUserExists = jest.fn();

  // 测试有效输入
  test('valid input triggers checkIfUserExists', () => {
    const name = 'John';
    const surname = 'Doe';
    const email = 'john.doe@example.com';
    const username = 'johndoe';
    const password = 'Passw0rd';

    checkIfBlank(name, surname, email, username, password, setErrorMessage, checkIfUserExists);

    expect(setErrorMessage).not.toHaveBeenCalled();
    expect(checkIfUserExists).toHaveBeenCalled();
  });

  // 测试字段为空
  test('missing field shows alert', () => {
    // 模拟alert方法
    window.alert = jest.fn();
    const name = '';
    const surname = 'Doe';
    const email = 'john.doe@example.com';
    const username = 'johndoe';
    const password = 'Passw0rd';

    checkIfBlank(name, surname, email, username, password, setErrorMessage, checkIfUserExists);

    expect(window.alert).toHaveBeenCalledWith('Please enter all details');
    expect(setErrorMessage).not.toHaveBeenCalled();
    expect(checkIfUserExists).not.toHaveBeenCalled();
  });

  // 测试无效邮箱
  test('invalid email sets error message', () => {
    const name = 'John';
    const surname = 'Doe';
    const email = 'johndoe@examplecom';
    const username = 'johndoe';
    const password = 'Passw0rd';

    checkIfBlank(name, surname, email, username, password, setErrorMessage, checkIfUserExists);

    expect(setErrorMessage).toHaveBeenCalledWith('Please enter a valid email');
    expect(checkIfUserExists).not.toHaveBeenCalled();
  });

  // 测试弱密码
  test('weak password sets error message', () => {
    const name = 'John';
    const surname = 'Doe';
    const email = 'john.doe@example.com';
    const username = 'johndoe';
    const password = 'password';

    checkIfBlank(name, surname, email, username, password, setErrorMessage, checkIfUserExists);

    expect(setErrorMessage).toHaveBeenCalledWith('Please enter a stronger password');
    expect(checkIfUserExists).not.toHaveBeenCalled();
  });
});

为什么之前的导出方法无效

之前尝试的导出方式(如在组件内部写export const checkIfBlank、export {checkIfBlank}等)无法生效,因为原函数定义在Register组件函数的内部,属于组件闭包的私有变量,外部模块无法直接访问。只有将函数移到组件外部,成为独立的模块级函数,才能成功导出并被测试代码引用。

内容的提问来源于stack exchange,提问作者Mu'aaz Bassa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:24:56