Jest / React-如何在函数中测试useState状态更新的结果

时间:2020-02-22 14:46:45

标签: javascript reactjs unit-testing asynchronous jestjs

我有一个简单的购物车功能,当用户单击以增加或减少购物车中商品的数量时,调用useState函数来更新状态下的购物车数量。

const [cart, setCart] = useState([]);

const onUpdateItemQuantity = (cartItem, quantityChange) => {
    const newCart = [...cart];
    const shouldRemoveFromCart = quantityChange === -1 && cartItem.count === 1;
    ...
    if (shouldRemoveFromCart) {
      newCart.splice(cartIndex, 1);
    } else {
      ...
    }
    setCart(newCart); //the useState function is called
}

所以开个玩笑,我有一个函数可以测试用户何时将购物车中的商品数量设置为零,但它尚未从购物车中删除该商品,我想是因为它尚未收到{ {1}}:

setCart(newCart)

此测试通过,因为购物车现在包含其数量降至零的项目。但是实际上不应该这样,因为从test('on decrement item from 1 to 0, remove from cart', () => { const [cartItemToDecrement] = result.current.cartItems; const productToDecrement = result.current.products.find( p => p.id === cartItemToDecrement.id ); act(() => { result.current.decrementItem(cartItemToDecrement); }); act(() => { result.current.decrementItem(cartItemToDecrement); }); ... expect(result.current.cartItems).toEqual( expect.arrayContaining([ expect.objectContaining({ id: cartItemToDecrement.id, count: cartItemToDecrement.count - 2, inventory: productToDecrement.inventory + 2 }) ]) ); }); }); 中的splice操作开始,我们的onUpdateItemQuantity数组现在应该根本不包含对象。我如何在玩笑测试中验证此删除操作正在发生(反应代码正常工作)。

4 个答案:

答案 0 :(得分:1)

由于您提供的上下文对我来说还不够,因此我不太了解测试与您的onUpdateItemQuantity之间的关系。

但是从您的问题来看,有2条线索可以帮助您节省时间。

  1. 您可能知道setCart中的useState是不同步的,因此,如果您尝试在同一帧中从cart访问useState,即使您确保setCart被调用,也不会反映出更改。 useEffect是一个很好的解决方案。您可以找到useEffect [https://reactjs.org/docs/hooks-effect.html][1]的文档,任何更改都应反映在useEffect中,也许您可​​以在此处进行测试。

  2. 添加一个额外的变量myCart来存储cart和一个函数setMyCart。而不是在代码中调用setCart,而是调用setMyCartsetMyCart就像是

  function setMyCart(newCart)
  {
     myCart = newCart;
     setCart(myCart); // this is for triggering React re-render
  }

然后使用myCart可以立即反映出更改以进行测试。

第二点附加代码的唯一目的是,当我们仍然依赖React的重新渲染机制时,我们将自己的Model myCart用于我们的特定逻辑,而不是{ React的{1}}状态不仅适用于cart,而且在不适当的情况下也用于我们的逻辑。

答案 1 :(得分:0)

测试状态变化是一个挑战,因为它不会产生任何特定的输出(除非影响您的渲染)。

与测试React相比,关键是将测试范围缩小到您的职责范围。 React负责确保useState正确更新状态。您的责任是确保正确使用数据并将正确的数据发送回去。因此,不用测试useState,而是测试组件如何响应给定的状态,并确保设置正确的状态。

模拟useState的答案。为它提供一个返回传递给它的初始值的实现,并带有一个模拟函数,使您可以读取正在保存的数据。

更详细的答案在这里:

https://dev.to/theactualgivens/testing-react-hook-state-changes-2oga

HTH!

答案 2 :(得分:0)

目前还不清楚onUpdateItemQuantity的调用方式。甚至它的确切作用。即便如此,您声称toEqual(...[{...,count:cartItemToDecrement.count - 2,...}]...)通过了。这可能意味着onUpdateItemQuantitydecrementItem内部被调用,在onUpdateItemQuantity内部被称为newCart[cartIndex].count--,如果toEqual()通过,则意味着{{ 1}}成功更新了状态,并且在setState() 您具有已实现的状态之后。并且如果测试的名称正确,并且初始值为act(),则应表示cartItemToDecrement.count === 1,并且您永远都不会进入cartItemToDecrement.count - 2 === -1。所以也许问题不在于测试,而在于代码。

答案 3 :(得分:0)

如果您需要使用useState()测试每个状态更新,则可以通过实现一个useEffect函数来进行此操作,该函数专门侦听该状态更改,例如您的购物车:

useEffect(() => {
    test(...)
), [cart]);

因此您可以在useEffect()函数中进行测试。

这将确保您拥有正确的cart值,并且每次购物车更新时都会调用它。