首页 > 解决方案 > React - 当父组件将方法作为道具传递时,在哪里对状态变化进行单元测试:父组件还是子组件?

问题描述

我正在尝试找出对以下情况进行单元测试的最佳方法

问题是:我是否测试App.tsxAttendees.tsx中的状态是否正确更改?我已经看到了应该测试父组件中的状态更改的示例,但是这些示例表明父组件在 DOM 中而不是在子组件中显示值。

代码如下

应用程序.tsx

import React, { FC, Component } from 'react';
import {Attendees} from './Attendees';


interface AppState {
  attendees: number
}
export default class App extends Component<{}, AppState> {
  constructor(props:any) {
    super(props);
    this.handleAttendeesChange = this.handleAttendeesChange.bind(this);
    this.state = {
      attendees: 0
    };
  }

  handleAttendeesChange(value: number) {
    this.setState({ attendees: this.state.attendees + value});
  }
  
  render() {
    return (
      <div>
        <h1>Parent Component</h1>
        <Attendees currentCount={this.state.attendees} onValueChange={this.handleAttendeesChange} />
      </div>
    )
  }
}

与会者.tsx

import React, { FC } from 'react';

type AttendeesProps = {
  currentCount: number,
  onValueChange: (value: number) => void
}

export const Attendees:FC<AttendeesProps> = ({ currentCount, onValueChange }) => {
  return (
      <div>
        <button data-testid="countUp" onClick={() => onValueChange(1)}>
          Up
        </button >
        <button data-testid="countDown" onClick={() => onValueChange(-1)}>
          Down
        </button >
        <p data-testid="currentCount">
          {currentCount}
        </p>
        
      </div>
    
  )
}

这是我目前使用 react-testing-library 在 Attendeees.test.tsx 中测试的内容

import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { unmountComponentAtNode } from "react-dom";
// import { act } from "react-dom/test-utils";
import {Attendees} from './Attendees';
let container: any = null;
beforeEach(() => {
  // setup a DOM element as a render target
  container = document.createElement("Main");
  document.body.appendChild(container);
});

afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});
it("Attendees should respond to callback props", () => {
  const onValueChange = jest.fn();
  const { getByTestId } = render(<Attendees currentCount={0} onValueChange={onValueChange} />, container)

  fireEvent.click(getByTestId('countUp'))
  expect(onValueChange).toBeCalledWith(1);
  expect(onValueChange).toHaveBeenCalledTimes(1);
  expect(getByTestId('currentCount').textContent).toBe('1');

})

标签: reactjsreact-testing-library

解决方案


迟到的答案,但可能会帮助有同样问题的人。

“我是否在 App.tsx 或 Attendees.tsx 中测试状态是否正确更改?”

我们不必测试是否setState正常工作。但是,如果我们想测试更新的计数是否显示在参加者组件中,我们可以在 中进行测试App.test.tsx,因为那是状态所在的位置。

结合更多关于测试的想法

我们不必在 中创建容器beforeEach然后将其删除afterEachreact-testing-library照顾它。

这是我的 Attendees.test.tsx 版本:

// Attendees.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { Attendees } from './Attendees';

describe('Attendees component', () => {
  it('should respond to Up button click', () => {
    const onValueChange = jest.fn();
    render(<Attendees currentCount={0} onValueChange={onValueChange} />);

    // Don't have to use data-testid all the time. Here's another way
    fireEvent.click(screen.getByText('up', { exact: false }));
    expect(onValueChange).toBeCalledWith(1);
    expect(onValueChange).toHaveBeenCalledTimes(1);
  });

  it('should respond to Down button click', () => {
    const onValueChange = jest.fn();
    render(<Attendees currentCount={0} onValueChange={onValueChange} />);

    fireEvent.click(screen.getByText('down', { exact: false }));
    expect(onValueChange).toBeCalledWith(-1);
    expect(onValueChange).toHaveBeenCalledTimes(1);
  });
});

我会将检查当前计数显示的测试放在 App.test.tsx 中:

// App.test.tsx
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import App from './App';

describe('App component', () => {

  it('should display increased attendee count when the Up button is clicked', () => {
    render(<App />);

    // Sometimes it may be a good idea to ensure that the precondition is true
    expect(screen.getByLabelText('current-attendee-count').textContent).toBe('0');

    fireEvent.click(screen.getByText('up', { exact: false }));

    expect(screen.getByLabelText('current-attendee-count').textContent).toBe('1');
  });
});

笔记

上面的示例使用 React v17.0.1 和 @testing-library/react v11.2.3 进行了测试。


推荐阅读