如何模拟包装器中同一组件上的函数触发的响应?

How to mock out a response triggered by a function on the same component in a wrapper?

我的组件有一个在点击保存按钮时触发的功能。然后基于在包装器中完成提取,然后再次将提取响应作为 prop 向下传递。所以 putFn 属性 接受一个函数,putResponse 接受一个 Promise.

我想模拟包装器并在此测试中仅关注组件,在此示例中为“myComponent”。

给定以下测试设置:

./MyComponent.test.js

function setup() {
  let mockPutResponse;
  const putMockFn = jest.fn(() => {
    mockPutResponse = Promise.resolve(
      JSON.stringify({ success: true, loading: false })
    );
  });
  render(<MyComponent putFn={putMockFn} putResponse={mockPutResponse} />);
  return { putMockFn };
}

test("MyComponent saves the stuff", async () => {
  const { putMockFn } = setup();

  const button = screen.getByRole("button", { name: /save changes/i });
  userEvent.click(button);

  // this passes
  expect(putMockFn).toHaveBeenCalled();

  // this does not pass since the component shows this message
  // based on the putResponse property
  expect(await screen.findByText(/saved successfully/i)).toBeInTheDocument();
});

如何模拟传递到 putResponse 的 return 值 属性?

我要测试的组件是这样的:

./MyComponent.js

import React from "react";

const MyComponent = ({ putFn, putResponse }) => {
  return (
    <form onSubmit={putFn}>
      {putResponse?.loading && <p>Loading...</p>}
      {putResponse?.success && <p>saved succesfully</p>}
      <label htmlFor="myInput">My input</label>
      <input name="myInput" id="myInput" type="text" />
      <button>Save changes</button>
    </form>
  );
};

export default MyComponent;

它被一种包装器使用,类似于:

./App.js(任意码)

import React, { useState } from "react";
import MyComponent from "./MyComponent";

export default function App() {
  const [wrapperPutResponse, setWrapperPutResponse] = useState();
  const handlePut = e => {
    e.preventDefault();
    setWrapperPutResponse({ loading: true });
    // timeout, in the actual app this is a fetch
    setTimeout(function() {
      setWrapperPutResponse({ success: true, loading: false });
    }, 3000);
  };

  return <MyComponent putFn={handlePut} putResponse={wrapperPutResponse} />;
}

创建沙箱:codesandbox.io/s/bold-cloud-2ule8?file=/src/MyComponent.test.js

您可以创建一个 Wrapper 组件来渲染和控制 MyComponent

import React, { useState, useEffect } from "react";
import { screen, render } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import MyComponent from "./MyComponent";


const mockPutResponse = jest.fn()

function setup() {

  const Wrapper = () => {
    const [clicked, setClicked] = useState(false)
    const response = clicked ? { success: true, loading: false} : null
    useEffect(() => {
      mockPutResponse.mockImplementation(() => {
        setClicked(true)
      })
    }, [])
    return <MyComponent putFn={mockPutResponse} putResponse={response} />
  }

  render(<Wrapper />);
}

test("MyComponent saves the stuff", async () => {
  setup()
  // expect(await screen.findByText(/loading.../i)).toBeInTheDocument();

  const button = screen.getByRole("button", { name: /save changes/i });
  userEvent.click(button);

  // this passes
  expect(mockPutResponse).toHaveBeenCalled();

  // had some issue with expect acting up when using the toBeInDocument assertion 
  // so I used this one instead
  const text = await screen.findByText(/saved succesfully/i)
  expect(text).toBeTruthy()
});

Codesandbox