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

pin*_*ndi 9 reactjs react-testing-library

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

\n

我的组件:

\n
import React, { useState } from 'react';\nimport { Redirect } from 'react-router-dom';\nimport { connect } from 'react-redux';\nimport PropTypes from 'prop-types';\n\nimport { login } from 'actions/auth';\nimport Wrapper from './styled/Wrapper';\nimport Button from './styled/Button';\nimport Title from './styled/Title';\nimport { FormWrapper, StyledForm, FormField } from './styled/Form'\nimport Input from './styled/Input';\nimport Link from './styled/Link';\n\n\nconst Login = (props) => {\n    const [formData, setFormData] = useState({\n        email: '',\n        password: ''\n    });\n\n    const { email, password } = formData;\n\n    const onChange = e => setFormData({ ...formData, [e.target.name]: e.target.value });\n\n    const onSubmit = e => {\n        e.preventDefault();\n        props.login(email, password);\n    }\n\n    if (props.isAuthenticated) {\n        return <Redirect to='/' />\n    }\n\n    return (\n        <Wrapper color='#F2F2F2'>\n            <FormWrapper>\n                <Title>Log In</Title>\n                <StyledForm data-testid='auth-form' onSubmit={e => onSubmit(e)}>\n                    <FormField>\n                        <Input\n                            placeholder='Email'\n                            type='email'\n                            name='email'\n                            value={email}\n                            onChange={e => onChange(e)}\n                            required\n                        />\n                    </FormField>\n                    <FormField>\n                        <Input\n                            placeholder='Password'\n                            type='password'\n                            name='password'\n                            value={password}\n                            onChange={e => onChange(e)}\n                            required\n                        />\n                    </FormField>\n                    <Button type='submit' marginTop='6%'>LOG IN</Button>\n                </StyledForm>\n                <p>Don't have an account? <Link to='/register'>Sign Up!</Link> </p>\n            </FormWrapper>\n        </Wrapper>\n    )\n}\n\nLogin.propTypes = {\n    login: PropTypes.func.isRequired,\n    isAuthenticated: PropTypes.bool\n}\n\nconst mapStateToProps = state => ({\n    isAuthenticated: state.auth.isAuthenticated\n});\n\nexport default connect(mapStateToProps, { login })(Login);\n
Run Code Online (Sandbox Code Playgroud)\n

我的测试文件:

\n
import React from 'react';\nimport { render, cleanup, fireEvent } from '@testing-library/react';\nimport { Provider } from 'react-redux';\nimport { BrowserRouter as Router } from 'react-router-dom';\nimport store from '../../store';\n\nimport Login from '../Login';\n\n\nafterEach(cleanup);\nconst onChange = jest.fn();\n\n\ntest('<Login>', () => {\n\n    const { queryByTestId, getByPlaceholderText, getByText, debug } =\n        render(\n            <Provider store={store}>\n                <Router>\n                    <Login />\n                </Router>\n            </Provider>);\n\n    expect(queryByTestId('auth-form')).toBeTruthy();\n\n    fireEvent.change(getByPlaceholderText('Email'), {\n        target: { value: 'test@yahoo.com' },\n    });\n    fireEvent.change(getByPlaceholderText('Password'), {\n        target: { value: 'testpassword' },\n    });\n\n    fireEvent.click(getByText('LOG IN'));\n    expect(onChange).toHaveBeenCalledTimes(1);\n\n});\n
Run Code Online (Sandbox Code Playgroud)\n

测试输出:

\n
 FAIL  src/components/__tests__/Login.test.js\n  \xe2\x9c\x95 <Login> (125 ms)\n\n  \xe2\x97\x8f <Login>\n\n    expect(jest.fn()).toHaveBeenCalledTimes(expected)\n\n    Expected number of calls: 1\n    Received number of calls: 0\n\n      32 | \n      33 |     fireEvent.click(getByText('LOG IN'));\n    > 34 |     expect(onChange).toHaveBeenCalledTimes(1);\n         |                      ^\n      35 | \n      36 | });\n\n      at Object.<anonymous>.test (src/components/__tests__/Login.test.js:34:22)\n\nTest Suites: 1 failed, 1 total\nTests:       1 failed, 1 total\nSnapshots:   0 total\nTime:        6.013 s\nRan all test suites related to changed files.\n\nWatch Usage: Press w to show more.\n
Run Code Online (Sandbox Code Playgroud)\n

有没有一种方法可以使 onSubmit 函数可供测试访问,而无需将其作为 prop 传递?

\n

and*_*dak 4

React 测试库的要点是,编写类似于用户如何使用应用程序的测试非常容易。这是我们想要达到的目标,让我们充满信心,也是业界所提倡的。我们编写测试是为了提高对我们应用程序的信心,而不是为了扩大覆盖范围。

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

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

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

这将为您提供充分的信心,而不是测试函数是否被调用,这不会给您足够的信心,因为如果函数的实现不正确怎么办?