使用 Jest 使用钩子测试 React 功能组件 [英] Testing React Functional Component with Hooks using Jest

查看:25
本文介绍了使用 Jest 使用钩子测试 React 功能组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我正在从基于类的组件转向功能组件,但是在使用 jest/enzyme 为显式使用钩子的功能组件内部的方法编写测试时遇到了困难.这是我的代码的精简版.

So I'm moving away from class based components to functional components but am stuck while writing test with jest/enzyme for the methods inside the functional components which explicitly uses hooks. Here is the stripped down version of my code.

function validateEmail(email: string): boolean {
  return email.includes('@');
}

const Login: React.FC<IProps> = (props) => {
  const [isLoginDisabled, setIsLoginDisabled] = React.useState<boolean>(true);
  const [email, setEmail] = React.useState<string>('');
  const [password, setPassword] = React.useState<string>('');

  React.useLayoutEffect(() => {
    validateForm();
  }, [email, password]);

  const validateForm = () => {
    setIsLoginDisabled(password.length < 8 || !validateEmail(email));
  };

  const handleEmailChange = (evt: React.FormEvent<HTMLFormElement>) => {
    const emailValue = (evt.target as HTMLInputElement).value.trim();
    setEmail(emailValue);
  };

  const handlePasswordChange = (evt: React.FormEvent<HTMLFormElement>) => {
    const passwordValue = (evt.target as HTMLInputElement).value.trim();
    setPassword(passwordValue);
  };

  const handleSubmit = () => {
    setIsLoginDisabled(true);
      // ajax().then(() => { setIsLoginDisabled(false); });
  };

  const renderSigninForm = () => (
    <>
      <form>
        <Email
          isValid={validateEmail(email)}
          onBlur={handleEmailChange}
        />
        <Password
          onChange={handlePasswordChange}
        />
        <Button onClick={handleSubmit} disabled={isLoginDisabled}>Login</Button>
      </form>
    </>
  );

  return (
  <>
    {renderSigninForm()}
  </>);
};

export default Login;

我知道我可以通过导出来为 validateEmail 编写测试.但是如何测试 validateFormhandleSubmit 方法.如果它是基于类的组件,我可以将组件浅化并从实例中将其用作

I know I can write tests for validateEmail by exporting it. But what about testing the validateForm or handleSubmit methods. If it were a class based components I could just shallow the component and use it from the instance as

const wrapper = shallow(<Login />);
wrapper.instance().validateForm()

但这不适用于功能组件,因为无法通过这种方式访问​​内部方法.有什么方法可以访问这些方法,还是应该在测试时将功能组件视为黑盒?

But this doesn't work with functional components as the internal methods can't be accessed this way. Is there any way to access these methods or should the functional components be treated as a blackbox while testing?

推荐答案

在我看来,您不应该担心单独测试 FC 内的方法,而应该测试它的副作用.例如:

In my opinion, you shouldn't worry about individually testing out methods inside the FC, rather testing it's side effects. eg:

  it('should disable submit button on submit click', () => {
    const wrapper = mount(<Login />);
    const submitButton = wrapper.find(Button);
    submitButton.simulate('click');

    expect(submitButton.prop('disabled')).toBeTruthy();
  });

由于您可能正在使用异步的 useEffect,您可能希望将您的期望包装在 setTimeout 中:

Since you might be using useEffect which is async, you might want to wrap your expect in a setTimeout:

setTimeout(() => {
  expect(submitButton.prop('disabled')).toBeTruthy();
});

您可能想要做的另一件事是提取与与表单介绍纯函数交互无关的任何逻辑.例如:而不是:

Another thing you might want to do, is extract any logic that has nothing to do with interacting with the form intro pure functions. eg: instead of:

setIsLoginDisabled(password.length < 8 || !validateEmail(email));

你可以重构:

export const isPasswordValid = (password) => password.length > 8;
export const isEmailValid    = (email) => {
  const regEx = /^(([^<>()[]\.,;:s@"]+(.[^<>()[]\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/;

  return regEx.test(email.trim().toLowerCase())
}

登录组件.jsx

import { isPasswordValid, isEmailValid } from './Helpers';
....
  const validateForm = () => {
    setIsLoginDisabled(!isPasswordValid(password) || !isEmailValid(email));
  };
....

这样就可以分别测试isPasswordValidisEmailValid,然后在测试Login组件时,可以模拟你的导入.然后剩下要测试您的 Login 组件的唯一事情就是点击时,调用导入的方法,然后基于这些响应的行为例如:

This way you could individually test isPasswordValid and isEmailValid, and then when testing the Login component, you can mock your imports. And then the only things left to test for your Login component would be that on click, the imported methods get called, and then the behaviour based on those response eg:

- it('should invoke isPasswordValid on submit')
- it('should invoke isEmailValid on submit')
- it('should disable submit button if email is invalid') (isEmailValid mocked to false)
- it('should disable submit button if password is invalid') (isPasswordValid mocked to false)
- it('should enable submit button if email is invalid') (isEmailValid and isPasswordValid mocked to true)

这种方法的主要优点是 Login 组件应该只处理更新表单,而不处理其他任何事情.这可以非常直接地进行测试.任何其他逻辑,应该单独处理(关注点分离).

The main advantage with this approach is that the Login component should just handle updating the form and nothing else. And that can be tested pretty straight forward. Any other logic, should be handled separately (separation of concerns).

这篇关于使用 Jest 使用钩子测试 React 功能组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆