繁体   English   中英

测试setState匿名调用function

[英]Testing setState anonymous function call

考虑 JSX 中的以下按钮:

interface Props {
  index: number;
  setIndex: Dispatch<SetStateAction<number>>;
}

export function Button({ index, setIndex }: Props) {
  return (
    <button
      type="button"
      data-testid="prev_btn"
      onClick={() => setIndex(prevIndex => prevIndex - 1)}
    >
      Previous
    </button>
  );
}

我想在 Jest / React 测试库中编写一个测试来呈现按钮并将setIndex作为道具传递,然后检查是否使用正确的值调用了setIndex 例如:

const mockSetIndex = jest.fn();
const mockProps = {
  setIndex: mockSetIndex,
  index: 1,
};

describe('Button test', () => {
  it('should update the index when the "previous" button is clicked', () => {
    const { getByTestId } = render(<Button {...mockProps} />);
    const prevBtn = getByTestId('prev_btn');

    userEvent.click(prevBtn);

    expect(mockSetIndex).toBeCalledWith(0);
  });
});

我得到的结果是失败的:

    expect(jest.fn()).toBeCalledWith(...expected)

    Expected: 0
    Received: [Function anonymous]

还有另一个增加索引的类似按钮的实例,所以我想测试这两个按钮的行为是否正确。

您使用setIndex而不是数字调用 setIndex。 虽然这是setIndex可接受的论点,但它不是您要测试的内容。 将您的 function 更改为类似

interface Props {
  index: number;
  setIndex: Dispatch<SetStateAction<number>>;
}

export function Button({ index, setIndex }: Props) {
  return (
    <button
      type="button"
      data-testid="prev_btn"
      onClick={() => setIndex(index - 1)}
    >
      Previous
    </button>
  );
}

或者测试它是否传递了具有预期行为的 function

describe('Button test', () => {
  it('should update the index when the "previous" button is clicked', () => {
    const { getByTestId } = render(<Button {...mockProps} />);
    const prevBtn = getByTestId('prev_btn');

    userEvent.click(prevBtn);

    const passedFunction = mockProps.setIndex.mock.calls[0][0]

    expect(passedFunction(1)).toEqual(0);
  });
});

根据Button的属性,我猜它的用法看起来像这样:

const Parent = () => {
  const [index, setIndex] = useState(1);
  return (
    <>
      <Button index={index} setIndex={setIndex} />
      <p data-testid="current_index">Current index: {index}</p>
    </>
  );
};

这使Parent无法控制其自己的 state, Button可以调用例如setIndex(100) ,无论它是否具有任何实际意义。 由于这个原因,将 setter 直接传递给另一个组件通常是一种代码味道。


一个不太紧密耦合的结构,其中Parent负责它自己的 state,看起来像这样:

const Parent = () => {
  const [index, setIndex] = useState(1);

  const decrement = () => setIndex((previousIndex) => previousIndex - 1);

  return (
    <>
      <p>Current index: {index}</p>
      <Button onClick={decrement} />
    </>
  );
};

容易测试:

describe("Button", () => {
  it("should signal when the Previous button is clicked", () => {
    const onClick = jest.fn();
    render(<Button onClick={onClick} />);

    userEvent.click(screen.getByRole("button", { name: /previous/i }));

    expect(onClick).toHaveBeenCalled();
  });
});

(请注意,我正在遵循优先级指南,尽可能多地使用面向用户的、可访问的选择器。)


或者,如果ButtonParent中使用(如 state 上的高耦合级别所建议的那样),请在该上下文中对其进行测试:

describe("Parent", () => {
  it("should allow the previous thing to be selected", () => {
    render(<Parent />);

    userEvent.click(screen.getByRole("button", { name: /previous/i }));

    // assert on actual outcome of index changing, e.g.
    expect(screen.getByTestId("current_index")).toHaveTextContent("Current index: 0");
  });
});

在这种情况下,在Button与其Parent之间的边界进行测试并不合适。 要指示这种耦合,您可以将Button移动到定义Parent的模块中,而不是将其导出以供单独访问 - 将按钮已提取到单独组件的事实视为实现细节

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM