无法弄清楚为什么jest测试在单击fireEvent后给我旧的状态

yshpjwxd  于 2023-09-28  发布在  Jest
关注(0)|答案(1)|浏览(113)

我使用Jest和@testing-library/react,同时使用来自@apollo/client/testing的MockedProvider模拟数据。在试图弄清楚如何更改单击时发生的变化时,我在这个测试中发现了这个非常奇怪的行为:

  1. test('Test 1', async () => {
  2. mockAllIsIntersecting(true);
  3. renderTest(() => (
  4. <MockedApolloProvider
  5. mocks={[
  6. getProductMock({ code: '7340266_F671' }),
  7. appSettingsSalesDocumentMock(),
  8. ]}
  9. >
  10. <ProductCard
  11. product={productMock}
  12. language="no"
  13. translated={translatedMock}
  14. />
  15. </MockedApolloProvider>
  16. ));
  17. fireEvent.click(await screen.findByTitle('A'));
  18. const img = screen.getByAltText(
  19. 'B',
  20. );
  21. console.log(img);
  22. expect(img.getAttribute('src')).toEqual(
  23. 'C',
  24. );
  25. });

1.首先,单击事件假定更改图像(到图像2)
1.通过搜索“A”找到更改图像的链接(带有preventDefault的a-href标记)
1.然后我在上面做一个fireEvent.click
1.它应该从MockedApolloProvider获取模拟数据
1.图像1现在应该已经改变了源代码,但是当我试图在控制台中找到图像2时,图像仍然是旧的图像1。
但奇怪的是,如果我改变getByAltText('B'),其中B是DOM中没有的其他东西,测试打印出它不匹配,以及可用的DOM看起来像什么,现在突然变成了正确的Image(图2)。
它看起来像当screen.getByAltText('B')被成功地找到,点击事件得到恢复???
下面是正在使用的组件的Veery简化版本,试图包括重要/相关的部分:

  1. export const ProductCard: React.FC<ProductCardProps> = memo(
  2. ({ product }) => {
  3. const [productState, setProductState] = useState(product);
  4. const currProduct = productState;
  5. const { code, url, siblings } = currProduct;
  6. const loadProduct = (productCode: string) => {
  7. getProduct({
  8. variables: { variantCode: productCode },
  9. onCompleted: (data) => {
  10. data.getProduct && setProductState(data.getProduct);
  11. },
  12. });
  13. };
  14. const onSiblingClick = (
  15. productCode: string,
  16. event: React.MouseEvent<HTMLElement>,
  17. ) => {
  18. event.preventDefault();
  19. if (currProduct.code === productCode) {
  20. return false;
  21. }
  22. loadProduct(productCode);
  23. };
  24. return (
  25. <ProductCardContainer>
  26. <ProductCardImageContainer>
  27. <ProductCardHoverElements
  28. currProduct={currProduct}
  29. product={product}
  30. />
  31. </ProductCardImageContainer>
  32. {/* onSiblingClick gets triggered in the test*/}
  33. <ProductColors
  34. code={code}
  35. productUrl={url}
  36. siblings={siblings}
  37. onSiblingClick={onSiblingClick}
  38. numberOfVisibleSwatches={3}
  39. size="small"
  40. ariaLabelMoreColors="More colors"
  41. />
  42. </ProductCardContainer>
  43. );
  44. },
  45. (prevProps, nextProps) => {
  46. return prevProps.product.code === nextProps.product.code;
  47. },
  48. );

注意:图像1和图像2都有相同的“B”替换文本
注2:screen.findByTitle('A')是一个在组件中带有clickHandler的超链接(a-href)

7cjasjjr

7cjasjjr1#

不应该在act中 Package fireEvent的原因是它是多余的,因为fireEvent已经 Package 在act中了。然而,这里有一个重要的区别。你不仅仅是在act中 Package fireEvent,而是在异步行为中 Package 它并等待它:

  1. await act(async() => {
  2. await fireEvent.click('somthing');
  3. })

这与fireEvent Package 在其中的正常行为不同,不应该从esLint收到投诉。您可以在类型定义中看到不同的类型:

  1. // VoidOrUndefinedOnly is here to forbid any sneaky "Promise" returns.
  2. // the actual return value is always a "DebugPromiseLike".
  3. declare const UNDEFINED_VOID_ONLY: unique symbol;
  4. // tslint:disable-next-line: void-return
  5. type VoidOrUndefinedOnly = void | { [UNDEFINED_VOID_ONLY]: never };
  6. /**
  7. * Wrap any code rendering and triggering updates to your components into `act()` calls.
  8. *
  9. * Ensures that the behavior in your tests matches what happens in the browser
  10. * more closely by executing pending `useEffect`s before returning. This also
  11. * reduces the amount of re-renders done.
  12. *
  13. * @param callback An asynchronous, void callback that will execute as a single, complete React commit.
  14. *
  15. * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks
  16. */
  17. // VoidOrUndefinedOnly is here to forbid any sneaky return values
  18. export function act(callback: () => Promise<VoidOrUndefinedOnly>): Promise<undefined>;
  19. /**
  20. * Wrap any code rendering and triggering updates to your components into `act()` calls.
  21. *
  22. * Ensures that the behavior in your tests matches what happens in the browser
  23. * more closely by executing pending `useEffect`s before returning. This also
  24. * reduces the amount of re-renders done.
  25. *
  26. * @param callback A synchronous, void callback that will execute as a single, complete React commit.
  27. *
  28. * @see https://reactjs.org/blog/2019/02/06/react-v16.8.0.html#testing-hooks
  29. */
  30. export function act(callback: () => VoidOrUndefinedOnly): DebugPromiseLike;
  31. // Intentionally doesn't extend PromiseLike<never>.
  32. // Ideally this should be as hard to accidentally use as possible.
  33. export interface DebugPromiseLike {
  34. // the actual then() in here is 1-ary, but that doesn't count as a PromiseLike.
  35. then(onfulfilled: (value: never) => never, onrejected: (reason: never) => never): never;
  36. }

除了在async act中 Package fireEvent之外,你还可以经常使用waitFor来处理它后面的expect。我现在稍微喜欢这个。

  1. fireEvent.click(await screen.findByTitle('A'));
  2. await waitFor(() => {
  3. expect(screen.getByAltText('B').getAttribute('src')).toEqual('C');
  4. })
展开查看全部

相关问题