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

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

问题描述

因此,我正在从基于类的组件转移到功能性组件,但是在用笑话/酶为功能性组件中显式使用钩子的方法编写测试时陷入困境。这是我的代码的精简版本。

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 编写测试通过导出。但是如何测试 validateForm handleSubmit 方法呢?如果它是基于类的组件,那么我可以将其变浅,然后从实例中将其用作

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



LoginComponent.jsx



LoginComponent.jsx

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

这样,您可以单独测试 isPasswordValid isEmailValid ,然后在测试 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使用Hooks测试React功能组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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