带有 UseReducer 和 UseContext 的 React 测试库未正确更新状态

时间:2021-06-03 04:52:03

标签: reactjs jestjs react-testing-library use-context use-reducer

我创建了一个沙箱来概述主要的兴趣点: https://codesandbox.io/s/restless-dawn-nwy0l

请忽略格式,因为这只是我放在一起的 MWE。

当我在上面的沙箱中运行以下 test

 
import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";

import Statistics from "../src/components/Block/Statistics";
import { IAction, IState } from "../src/typings/AppTypes";
import { AppReducer } from "../src/reducers/AppReducer";
import { AppContext } from "../src/context/AppContext";
import * as MineUtil from "../src/utils/mine";
import * as ConversionUtil from "../src/utils/conversion";

const state: IState = {
  verifiedTrans: [
    {
      to: "A",
      from: "B",
      amount: 1.23,
      message: "First Transaction",
      signature: "AB1.23"
    },
    {
      to: "A",
      from: "C",
      amount: 456.78,
      message: "Second Transaction",
      signature: "AC456.78"
    },
    {
      to: "A",
      from: "D",
      amount: 999.99,
      message: "Third Transaction",
      signature: "AD999.99"
    },
    {
      to: "A",
      from: "E",
      amount: 987.65,
      message: "Forth Transaction",
      signature: "AE987.65"
    },
    {
      to: "A",
      from: "F",
      amount: 1.01,
      message: "Fifth Transaction",
      signature: "AF1.01"
    }
  ],
  selectedTrans: [
    {
      to: "A",
      from: "C",
      amount: 456.78,
      message: "Second Transaction",
      signature: "AC456.78"
    }
  ],
  chain: [
    {
      index: 0,
      prevHash: "",
      currHash: new Array(64).fill("0").join(""),
      transactions: [],
      timestamp: Date.parse("04/31/2021"),
      merkleRoot: "",
      valid: true
    },
    {
      index: 1,
      prevHash: new Array(64).fill("0").join(""),
      currHash: new Array(64).fill("A").join(""),
      transactions: [
        {
          to: "A",
          from: "E",
          amount: 987.65,
          message: "Forth Transaction",
          signature: "AE987.65"
        }
      ],
      timestamp: Date.parse("05/01/2021"),
      merkleRoot: "987.65EForthTransactionAE987.65A",
      valid: true
    }
  ],
  preview: {
    index: 2,
    timestamp: Date.parse("05/02/2021"),
    prevHash: new Array(64).fill("A").join(""),
    currHash: "",
    transactions: [],
    merkleRoot: "",
    valid: false
  }
};

const dispatch = (action: IAction) => AppReducer(state, action);

it("keeps mining button disabled after mining due to valid solution", async () => {
  const solution =
    "000a4fda363405b2796986a63e8cedde080e1f29ed774f5f93bd97c42b9a96fc0";
  const target =
    "000b4fda363405b2796986a63e8cedde080e1f29ed774f5f93bd97c42b9a96fc0";

  jest.spyOn(MineUtil, "createTarget").mockReturnValue(Promise.resolve(target));
  jest
    .spyOn(ConversionUtil, "digestMessage")
    .mockReturnValue(Promise.resolve(solution));

  render(
    <AppContext.Provider value={{ state, dispatch }}>
      <Statistics chain={false} />
    </AppContext.Provider>
  );

  expect(screen.getByRole("button", { name: /Block Mine/i })).toBeEnabled();

  // need to await state changes
  fireEvent.click(screen.getByRole("button", { name: /Block Mine/i }));
  await waitFor(() => {
    expect(screen.getByRole("button", { name: /Block Mine/i })).toBeDisabled();
  });

  await waitFor(() => {
    expect(
      screen.getByRole("textbox", { name: /Block Solution/i })
    ).toHaveClass("valid-solution");
  });
});

 
 

我明白了:

result of test

该测试只是检查如果您在选定交易后按下挖掘按钮,该按钮将变为禁用状态,并且一旦挖掘出有效解决方案,它的类别就会发生变化,从而变为绿色文本(对于此组件)。由于解决方案有效,该按钮也应保持禁用状态。

此外,预览块将变为绿色,但这超出了本单元测试的范围。

但是,似乎状态预览块(它确定解决方案输入文本框上的类)在测试环境中没有正确更新。在开发过程中,情况并非如此,一切都按预期进行。

有什么建议/提示吗?

1 个答案:

答案 0 :(得分:2)

在您的测试中,您设置了 value, dispatch 的模拟版本,当您点击 Block mine 按钮时不会触发更新。

但在实际代码中,在 App.tsx 中,您使用 useReducer 钩子(在深处它会触发重新渲染,并且新的 props 通过 Context 传递给 Statistics)。

要修复它,只需使用 useReducer

模拟您的测试
  const Wrapper = () => {
    const [state, dispatch] = useReducer(AppReducer, inititalValues);

    return (
      <AppContext.Provider value={{ state, dispatch }}>
        <Statistics chain={false} />
      </AppContext.Provider>
    );
  };

  render(<Wrapper />);

inititalValues 是第 12 行的变量 state

工作沙盒 here

输出: enter image description here