如何使用 React 测试库测试组件内部功能

时间:2021-01-21 04:08:59

标签: reactjs react-testing-library

我是测试新手,我正在尝试使用 react 测试库来测试我的应用程序。我遇到的第一个问题是测试组件内部的函数,例如事件处理程序。我发现了一些函数(事件处理程序)作为 props 在组件中传递的例子,但如果可能的话,我不想这样做。

我的组件:

import React, { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import { login } from 'actions/auth';
import Wrapper from './styled/Wrapper';
import Button from './styled/Button';
import Title from './styled/Title';
import { FormWrapper, StyledForm, FormField } from './styled/Form'
import Input from './styled/Input';
import Link from './styled/Link';


const Login = (props) => {
    const [formData, setFormData] = useState({
        email: '',
        password: ''
    });

    const { email, password } = formData;

    const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value });

    const onSubmit = e => {
        e.preventDefault();
        props.login(email, password);
    }

    if (props.isAuthenticated) {
        return <Redirect to='/' />
    }

    return (
        <Wrapper color='#F2F2F2'>
            <FormWrapper>
                <Title>Log In</Title>
                <StyledForm data-testid='auth-form' onSubmit={e => onSubmit(e)}>
                    <FormField>
                        <Input
                            placeholder='Email'
                            type='email'
                            name='email'
                            value={email}
                            onChange={e => onChange(e)}
                            required
                        />
                    </FormField>
                    <FormField>
                        <Input
                            placeholder='Password'
                            type='password'
                            name='password'
                            value={password}
                            onChange={e => onChange(e)}
                            required
                        />
                    </FormField>
                    <Button type='submit' marginTop='6%'>LOG IN</Button>
                </StyledForm>
                <p>Don't have an account? <Link to='/register'>Sign Up!</Link> </p>
            </FormWrapper>
        </Wrapper>
    )
}

Login.propTypes = {
    login: PropTypes.func.isRequired,
    isAuthenticated: PropTypes.bool
}

const mapStateToProps = state => ({
    isAuthenticated: state.auth.isAuthenticated
});

export default connect(mapStateToProps, { login })(Login);

我的测试文件:

import React from 'react';
import { render, cleanup, fireEvent } from '@testing-library/react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import store from '../../store';

import Login from '../Login';


afterEach(cleanup);
const onChange = jest.fn();


test('<Login>', () => {

    const { queryByTestId, getByPlaceholderText, getByText, debug } =
        render(
            <Provider store={store}>
                <Router>
                    <Login />
                </Router>
            </Provider>);

    expect(queryByTestId('auth-form')).toBeTruthy();

    fireEvent.change(getByPlaceholderText('Email'), {
        target: { value: 'test@yahoo.com' },
    });
    fireEvent.change(getByPlaceholderText('Password'), {
        target: { value: 'testpassword' },
    });

    fireEvent.click(getByText('LOG IN'));
    expect(onChange).toHaveBeenCalledTimes(1);

});

测试输出:

 FAIL  src/components/__tests__/Login.test.js
  ✕ <Login> (125 ms)

  ● <Login>

    expect(jest.fn()).toHaveBeenCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0

      32 | 
      33 |     fireEvent.click(getByText('LOG IN'));
    > 34 |     expect(onChange).toHaveBeenCalledTimes(1);
         |                      ^
      35 | 
      36 | });

      at Object.<anonymous>.test (src/components/__tests__/Login.test.js:34:22)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        6.013 s
Ran all test suites related to changed files.

Watch Usage: Press w to show more.

有没有办法让测试可以访问 onSubmit 函数而无需将其作为道具传递?

1 个答案:

答案 0 :(得分:1)

React 测试库的主要观点是编写类似于用户如何使用应用程序的测试非常容易。这就是我们想要达到的目标,以给予我们充分的信心,这也是业界所提倡的。我们编写测试是为了提高对应用程序的信心,而不是提高覆盖率。

考虑到上述情况,您不应该使用toHaveBeenCalledTimes 代替,您应该测试触发onChange 函数的视觉效果。根据您分享的代码,我不知道成功登录后会呈现什么,但对于我们的用例,假设文本 Welcome, User! 呈现。

您可以像现在一样触发一个事件,而不是使用 haveBeenCalled 进行测试,而是使用以下内容:

expect(getByText('Welcome, User!').toBeInTheDocument();

这会让您充满信心,而不是测试是否调用了一个函数,这不会给您足够的信心,因为如果函数的实现不正确怎么办?