首页 > 解决方案 > 如何测试使用钩子且不返回 JSX 的 ReactJS 函数

问题描述

我正在尝试在 React 中进行功能测试,但我遇到了一些障碍,我希望有人能对此有所了解。目前,我有一个小计数器应用程序,它有一个按钮组件,它接收一个函数和一个字符串作为道具。看起来像这样:

按钮.js

import React from 'react'
import PropTypes from 'prop-types'

export const Button = (props) => {
  const { btnTitle, btnAction } = props

  return (
    <button onClick={btnAction}>{btnTitle}</button>
  )
}

Button.propTypes = {
  btnAction: PropTypes.func,
  btnTitle: PropTypes.string
}

我还有一个 helpers 目录,其中包含我的 CounterHandler 函数,该函数接收一个数字作为计数器初始值,并且可以增加或减少所述初始值。代码如下所示:

CounterHandler.js

import React from 'react'

export const CounterHandler = num => {
  const [counter, setCounter] = React.useState(num)
  const increase = () => setCounter(counter + 1)
  const decrease = () => setCounter(counter - 1)

  return {
    counter,
    increase,
    decrease
  }
}

现在我的 App.js 呈现按钮和操作代码如下所示。

应用程序.js

import React from 'react'
import CounterHandler from './components/button/helpers'
import Button from './components/button'

function App () {
  const counter = CounterHandler(0)

  return (
    <div className="App">
      <Button btnTitle="click to increase" btnAction={counter.increase} />
      <Button btnTitle="click to decrease" btnAction={counter.decrease} />
      <h1>counter: {counter.counter}</h1>
    </div>
  )
}

export default App

该应用程序按预期工作。计数器将根据按下的按钮增加或减少。

现在我正在尝试为我的CounterHandler.js函数编写一个测试,但不幸的是,我一直遇到在本地服务器上运行应用程序时不会发生的钩子错误。

到目前为止,我想尝试的唯一测试是获取在我的计数器中找到的初始值并从那里继续。我的测试如下所示:

CounterHandler.test.js

import { CounterHandler } from './CounterHandler'

const counter = CounterHandler(0)

describe('Counter state', () => {
  test('test initial state', () => {
    expect(counter.counter).tobe(0)
  })
})

我得到的输出是:

在此处输入图像描述

有人可以给我一些指示吗?我将不胜感激。

为了进一步了解我的配置,这是我在这个项目中的实验性 GitHub 帐户。https://github.com/melvinalmonte/testing-practice

谢谢!!

标签: javascriptreactjsunit-testingjestjs

解决方案


如显示错误消息,useState应在反应组件内部调用反应挂钩(等)。但是在您的测试中,您在反应组件之外调用了它。

测试这种情况的推荐方法是:不要直接测试自定义钩子,与 react 组件一起测试。

例如 app.js

import React from 'react';
import { CounterHandler } from './counterHandler';

function App() {
  const counter = CounterHandler(0);

  return (
    <div className="App">
      <button name="increase" onClick={counter.increase}>
        click to increase
      </button>
      <button name="decrease" onClick={counter.decrease}>
        click to decrease
      </button>
      <h1>counter: {counter.counter}</h1>
    </div>
  );
}

export default App;

counterHandler.js

import React from 'react';

export const CounterHandler = (num) => {
  const [counter, setCounter] = React.useState(num);
  const increase = () => setCounter(counter + 1);
  const decrease = () => setCounter(counter - 1);

  return {
    counter,
    increase,
    decrease,
  };
};

app.test.js

import React from 'react';
import { shallow } from 'enzyme';
import App from './app';

describe('60158977', () => {
  let wrapper;
  beforeEach(() => {
    wrapper = shallow(<App></App>);
  });
  it('should render', () => {
    expect(wrapper.find('h1').text()).toBe('counter: 0');
  });

  it('should increase counter', () => {
    wrapper.find('button[name="increase"]').simulate('click');
    expect(wrapper.find('h1').text()).toBe('counter: 1');
  });

  it('should decrease counter', () => {
    wrapper.find('button[name="decrease"]').simulate('click');
    expect(wrapper.find('h1').text()).toBe('counter: -1');
  });
});

覆盖率 100% 的单元测试结果:

 PASS  stackoverflow/60158977/app.test.js
  60158977
    ✓ should render (9ms)
    ✓ should increase counter (3ms)
    ✓ should decrease counter (1ms)

-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |     100 |      100 |     100 |     100 |                   
 app.js            |     100 |      100 |     100 |     100 |                   
 counterHandler.js |     100 |      100 |     100 |     100 |                   
-------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        3.884s, estimated 4s

源代码:https ://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/60158977


推荐阅读