1

我有一个问题,即如何为下面的函数编写单元测试,函数是登录用户,具有一系列操作,例如用户输入数据、按下(假动作)按钮并向服务器发送请求和向用户响应数据使用 Jest 和 Enzyme 但我不知道该怎么做,还是我误解了 reactjs 中的单元测试?

export default function Login({ title }) {
  UseStyles(s, n);

  Login.propTypes = {
    title: PropTypes.string.isRequired,
  };

  const [email, setEmail] = useState('');
  const [pw, setPw] = useState('');
  const [error,setError] = useState('');
  const [checkLogin, setCheckLogin] = useState('');

  // set validation
  const [validEmail, setValidEmail] = useState('');
  const [validPw, setValidPw] = useState('');

  useEffect(() => {

    if(Cookie.get('token')){
      setCheckLogin('hadLogin');
      window.history.back()
    }
  },[]);

  // notification login success
  function Notify() {
    toast("Loading...", {
      position: "top-right",
      autoClose: 3000,
    });
  }

  function disableBtn() {
    const exist = Object.values(validator.fields).includes(false);
    if(exist){
      return true;
    }
    return false;
  }

  // when click login button
  // Want to write unit test
  function handleSubmit() { // this is funtion i want to write unit test
    if (!pw || !email)
    {
      !pw ? setError('Password is empty') : ''
      !email ? setError('Email is empty') : ''
      return;
    }
    AxiosConfig
      .post('/users/login', {
        email,
        password: pw,
      },)
      .then(res => {
        Cookie.set('token', res.data.token,{expires:1});
        Cookie.set('email', email,{expires:1});
        Cookie.set('roleName', 'user',{expires:1});
        Notify();
        window.location.href = `${BaseUrl}`
      })
      .catch(err => {
        setError(err.response.data);
      });
  }

  return (
    checkLogin !== 'hadLogin' ?
    (<div className={s.root}>
      <div className={s.container}>
        <h1>{title}</h1>
        <p className={s.lead}>
          Log in with your email address.
        </p>
        <div className={s.formGroup}>
          <a className={s.facebook}>
            <svg
              className={s.icon}
              width="30"
              height="30"
              viewBox="0 0 30 30"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path d="M22 16l1-5h-5V7c0-1.544.784-2 3-2h2V0h-4c-4.072 0-7 2.435-7 7v4H7v5h5v14h6V16h4z" />
            </svg>
            <span>Log in with Facebook</span>
          </a>
        </div>
        <div className={s.formGroup}>
          <a className={s.google}>
            <svg
              className={s.icon}
              width="30"
              height="30"
              viewBox="0 0 30 30"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d={
                  'M30 13h-4V9h-2v4h-4v2h4v4h2v-4h4m-15 2s-2-1.15-2-2c0 0-.5-1.828 1-3' +
                  '1.537-1.2 3-3.035 3-5 0-2.336-1.046-5-3-6h3l2.387-1H10C5.835 0 2 3.345 2 7c0' +
                  '3.735 2.85 6.56 7.086 6.56.295 0 .58-.006.86-.025-.273.526-.47 1.12-.47 1.735' +
                  '0 1.037.817 2.042 1.523 2.73H9c-5.16 0-9 2.593-9 6 0 3.355 4.87 6 10.03 6 5.882' +
                  '0 9.97-3 9.97-7 0-2.69-2.545-4.264-5-6zm-4-4c-2.395 0-5.587-2.857-6-6C4.587' +
                  '3.856 6.607.93 9 1c2.394.07 4.603 2.908 5.017 6.052C14.43 10.195 13 13 11' +
                  '13zm-1 15c-3.566 0-7-1.29-7-4 0-2.658 3.434-5.038 7-5 .832.01 2 0 2 0 1 0' +
                  '2.88.88 4 2 1 1 1 2.674 1 3 0 3-1.986 4-7 4z'
                }
              />
            </svg>
            <span>Log in with Google</span>
          </a>
        </div>
        <div className={s.formGroup}>
          <a className={s.twitter}>
            <svg
              className={s.icon}
              width="30"
              height="30"
              viewBox="0 0 30 30"
              xmlns="http://www.w3.org/2000/svg"
            >
              <path
                d={
                  'M30 6.708c-1.105.49-2.756 1.143-4 1.292 1.273-.762 2.54-2.56' +
                  '3-4-.97.577-2.087 1.355-3.227 1.773L25 5c-1.12-1.197-2.23-2-4-2-3.398 0-6' +
                  '2.602-6 6 0 .4.047.7.11.956L15 10C9 10 5.034 8.724 2 5c-.53.908-1 1.872-1' +
                  '3 0 2.136 1.348 3.894 3 5-1.01-.033-2.17-.542-3-1 0 2.98 4.186 6.432 7 7-1' +
                  '1-4.623.074-5 0 .784 2.447 3.31 3.95 6 4-2.105 1.648-4.647 2.51-7.53 2.51-.5' +
                  '0-.988-.03-1.47-.084C2.723 27.17 6.523 28 10 28c11.322 0 17-8.867 17-17' +
                  '0-.268.008-.736 0-1 1.2-.868 2.172-2.058 3-3.292z'
                }
              />
            </svg>
            <span>Log in with Twitter</span>
          </a>
        </div>
        <strong className={s.lineThrough}>OR</strong>
        <form method="post" >
          <div className={s.formGroup}>
            <label className={s.label} htmlFor="usernameOrEmail">
             Email address:
              <input
                className={s.input}
                id="usernameOrEmail"
                type="text"
                name="usernameOrEmail"
                maxLength={50}
                minLength={8}
                autoFocus // eslint-disable-line jsx-a11y/no-autofocus
                onChange={e => {setEmail(e.target.value)}}
                onBlur={e => setValidEmail(validator.fields.email)}
              />
              {/* Check validation */}
              {validator.message('email', email, 'required|email')}   {/* validator.message('field name', value to validate, 'validator rule') */}
              {/* Show validation error if having any */}
              <small className={s.validatorShow}>{validEmail === false ? validator.errorMessages.email : ''}</small>
            </label>
          </div>
          <div className={s.formGroup}>
            <label className={s.label} htmlFor="password">
              Password:
              <input
                className={s.input}
                id="password"
                type="password"
                name="password"
                onChange={e => setPw(e.target.value)}
                onBlur={e => {setValidPw(validator.fields.password); console.log(validator)}}
              />
               {/* Check validation */}
              {validator.message('password', pw, 'required|password')}
              {/* Show validation error if having any */}
              <small className={s.validatorShow}>{validPw === false ? validator.errorMessages.password : ''}</small>
            </label>
          </div>
          <div className={error ? s.alert : s.disable} >
              {error}
          </div>
          <div className={s.formGroup}>
          <ToastContainer/>
            <button className={disableBtn() ? s.buttonDisabled : s.button} type="button" onClick={handleSubmit} disabled={disableBtn()}>
              Log in
            </button>
          </div>
          <div className={s.link}>
            <a href="/forget">Forget Password</a>
          </div>
        </form>
      </div>
    </div>) : ''
  );
}
4

1 回答 1

1

您可以通过使用酶来安装组件并模拟执行要测试的代码的按钮单击来解决此问题。

取自https://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html并更改以适合您的代码:

const wrapper = mount(<Login title="test" />);

wrapper.find('.<INSERT WHATEVER s.button evaluates to>}').simulate('click');

现在你还需要用 jest 来模拟 Cookie 和 AxiosConfig。一种方法是使用 jest.spyOn():https ://jestjs.io/docs/en/jest-object#jestspyonobject-methodname以及一些模拟函数:https ://jestjs.io/docs/en/模拟函数 API

import AxiosConfig from 'whatever-npm-module"
import Cookie from 'whatever-npm-module"

const axiosSpy = jest.spyOn(AxiosConfig, 'post').mockResolvedValue({ data: { token: 'testToken' }});
const cookieSpy = jest.spyOn(Cookie, 'set');

您现在可以检查发送到 Cookie.set 的内容,以帮助验证您想要测试的内容和一些开玩笑的期望:https ://jestjs.io/docs/en/expect

expect(cookieSpy).toHaveBeenNthCalledWith(1, 'testToken', {expires:1});
expect(cookieSpy).toHaveBeenNthCalledWith(2, whateverEmailIs, {expires:1});
...

这是一个很好的起点,您可以更进一步,使用 beforeEach 和 afterEach 设置模拟,提供模拟返回值并清除这些返回值以测试您的错误场景。

于 2020-01-13T05:12:10.407 回答