首页 > 解决方案 > 如何使用 useState 挂钩设置初始状态

问题描述

我使用 useState 钩子编写了一个功能组件,该组件本身工作正常,但我在测试它时遇到了麻烦。我不明白如何在我的组件中创建初始状态。我尝试了多种模拟 useState 的方法,但都没有奏效:

describe('<Component/>', () => {
    beforeEach() = > {
        jest.spyOn(Readt, 'useState').mockImplementation( () => React.useState(['test']));
        //Also tried this:
        //React['useState'] = jest.fn().mockImplementation( () => React.useState(['test']));
    }
    it('should match snapshot', () => {
        expect(wrapper).toMatchSnapshot()
    }
}

我也看过这篇文章,但无法让它为我工作

如何在 jest 和酵素中为 useState Hook 设置初始状态?

我还读到您不应该在单元测试中模拟 useState 但是如果您不模拟它,您如何为组件设置初始状态?

谢谢。

编辑:关于下面的答案:

传入一个道具不会改变我试图测试的状态。

我有一个按钮,当单击(一种状态)时会呈现一个输入框,当用户输入(另一种状态)并按下确定按钮(另一种状态)时,会根据输入框中的内容创建一个按钮。

传递的道具有助于呈现按钮列表,但它无法控制用户在输入框中键入的内容。我正在尝试在输入框中设置一个状态,然后模拟单击确定按钮并对其进行测试。我不是想测试对本机 useState 功能的反应。我只是想使用已经具有“wrapper.setState”的酶,但是我该如何使用 useState 呢?

理想情况下,我想设置我的组件,设置用户在输入框中输入的状态,然后测试确定按钮是否完成了它应该做的事情。没有模拟之前的所有点击。

标签: reactjsjestjsreact-hooks

解决方案


组件的状态是它内部的,这是理解 React 组件的重要部分。一旦你“理解”了这一点,你就会明白,任何测试都不应该试图改变它。

但是你如何为你的组件设置一个初始状态呢?

您将一个值传递给您的useState钩子,在组件内。然后,您的测试应该使用该初始状态测试您的组件......他们应该只传递道具来更改它,就像您的非测试代码一样。

事实上,即使你可以以某种方式改变你的初始状态(这是不可能的 AFAIK),你的测试代码不应该做任何非测试代码所做的事情(除了设置模拟真实环境的测试环境),因为整个制作点测试是为了验证您的非测试代码是否有效。

同样,你永远不想测试你正在测试的任何东西的内部,无论是 React 组件还是其他任何东西。

如果你这样做,你编写的每一个测试都会在这些内部结构发生变化的那一刻中断,而测试价值的很大一部分是它们让你更容易重构(如果测试内部结构)。相反,您希望使用测试工具来测量组件的输出(即它生成的 HTML)。

再一次,这甚至与 React 无关。对任何事物的良好软件测试都不会试图弄乱该事物的内部结构,以改变或衡量它们。相反,好的测试侧重于外部,因为这又是所有非测试代码都可以使用,并且因为从概念上讲,您测试的所有内容都应该是您的测试代码的“黑匣子”。

编辑:回复评论/问题编辑

老实说,您的编辑并没有真正改变我的答案,只是它提醒我酶确实有办法改变测试中的状态(我自己从来没有使用过,我忘记了它的存在)。

但同样,仅仅因为某些事情是可能的并不意味着它是一个好主意,并且再次,你想测试你测试的任何东西的外部(不仅仅是 React 组件)。React 组件的状态就像函数内部的变量:它在该函数/组件内部,您应该避免直接测试。

相反,您要测试的是外部部分,并且(根据您的编辑)似乎不是props……但它仍然是外部的:DOM。

所以我建议做的是(再次)非测试代码正在做的事情:“点击”你的按钮。Enzyme 使用其“模拟”方法 ( https://airbnb.io/enzyme/docs/api/ReactWrapper/simulate.html ) 使这变得非常容易。

如果您只是模拟创建所需状态的按钮序列,您将只测试外部(即以与生产中使用相同的方式测试您的代码),但您将能够测试您想要的任何环境。


推荐阅读