如何使用反应测试库模拟将道具传递给子组件的父组件

How to mock a parent component which passes props to child component using react testing library

**如果传递给子组件的props是基于父组件中动态发生的状态变化,如何检查父组件中的动态状态变化并使用react测试库编写测试用例零件。有人可以帮我解决这个问题吗?

App.js

    import React, { Component } from 'react';
    import './App.css';
    import TextArea from './components/TextArea/TextArea';
    
    class App extends Component {
      constructor() {
        super();
        this.state = {
          textAreaParams: {},
        };
      }
      componentDidMount() {
        this.setDefaultTextAreaMessage();
      }
      setDefaultTextAreaMessage = () => {
        this.setState({
          textAreaParams: { message: 'Hello' },
        });
      };
      render() {
        const { textAreaParams } = this.state;
        return (
          <div className="App">
            {Object.keys(textAreaParams).length > 0 ? (
              <TextArea params={textAreaParams} />
            ) : null}
          </div>
        );
      }
    }
    
    export default App;

TextArea.js

    import { Component } from 'react';
    
    class TextArea extends Component {
      constructor(props) {
        super(props);
        this.state = {
          message: this.props.params.message,
        };
      }
      render() {
        return (
          <div>
            <textarea
              rows="4"
              cols="50"
              value={this.state.message ? this.state.message : ''}
              placeholder="test"
              onChange={() => {}}
            >
              {this.state.message}
            </textarea>
          </div>
        );
      }
    }
    
    export default TextArea;
   

App.test.js

    import App from './App';
    import { cleanup, render } from '@testing-library/react';
    
    describe('Rendering the App component and passing props to text area', () => {
      afterEach(cleanup);
      it('render the App component and check for the TextArea component', async () => {
        const props = { textAreaParams: { message: 'save' } };
        const { getByPlaceholderText } = render(<App {...props} />);
        const textAreaParams = getByPlaceholderText('test');
        expect(textAreaParams).toHaveTextContent('save');
      });
    });

我们需要将 onChange handler 属性从 App 组件传递给 TextArea,然后 TextArea 组件将在文本区域发生变化时调用该处理程序。

updateTextAreaMessage = (messageInTextArea) => {
  this.setState({
    textAreaParams: { message: messageInTextArea}
  })
}

在上面的代码中,messageInTextArea是一个字符串值,当我们改变TextArea中的文本时,当updateTextAreaMessage以相同的字符串值作为参数在TextArea组件中调用时,它会更新 App 组件中的状态。

完全实施:

App.js:

import React, { Component } from "react";
import './App.css';
import TextArea from './components/TextArea/TextArea';

class Main extends Component {
  constructor() {
    super();
    this.state = {
      textAreaParams: { message: "hello" } // we can provide default value here
    };
  }

  updateTextAreaMessage = (messageInTextArea) => {
    this.setState({
      textAreaParams: { message: messageInTextArea }
    });
  };

  render() {
    const { textAreaParams } = this.state;
    return (
      <div className="App">
        {Object.keys(textAreaParams).length > 0 ? (
          <TextArea
            params={textAreaParams}
            onUpdate={this.updateTextAreaMessage}
          />
        ) : null}

        <p aria-label="text area message">{textAreaParams.message}</p>
      </div>
    );
  }
}

export default Main;

TextArea.js:

import { Component } from "react";

class TextArea extends Component {
  render() {
    return (
      <div>
        <textarea
          rows="4"
          cols="50"
          value={this.props.params.message ? this.props.params.message : ""}
          placeholder="test"
          onChange={(event) => this.props.onUpdate(event.target.value)}
        >
          {this.props.params.message}
        </textarea>
      </div>
    );
  }
}

export default TextArea;

现在,我们将为 App.js 添加测试。但问题是这里要测试什么?答案是当 TextArea 组件的文本发生变化时,我们将添加状态是否更新的测试。
import { render } from "@testing-library/react";
import App from "./App";
import TextArea from './components/TextArea/TextArea';

describe("Rendering the App component and passing props to text area", () => {
  it("should render the App component with default message in TextArea", () => {
    const { getByPlaceholderText } = render(<Main />);
    const textAreaParams = getByPlaceholderText("test");
    expect(textAreaParams).toHaveTextContent(/hello/i);
  });

  it("should update the text area when we type something", () => {
    const { getByPlaceholderText, getByLabelText } = render(<Main />);
    userEvent.type(getByPlaceholderText("test"), "Anything");
    expect(getByLabelText(/text area message/i)).toHaveTextContent(/anything/i);
  });
});

describe("Rendering the Text Area component", () => {
  it("should render the TextArea component and calls onChange handlers when we type something", () => {
    const mockOnChangeHandler = jest.fn();
    const mockParams = { message: "save" };
    const { getByPlaceholderText } = render(
      <TextArea params={mockParams} onUpdate={mockOnChangeHandler} />
    );
    const inputTextArea = getByPlaceholderText("test");
    expect(inputTextArea).toHaveTextContent(/save/i);

    userEvent.type(inputTextArea, "Anything");
    expect(mockOnChangeHandler).toHaveBeenCalled();
  });
});