Skip to content

No waitForNextUpdate ? #1101

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
kamilzielinskidev opened this issue Jul 28, 2022 · 8 comments
Closed

No waitForNextUpdate ? #1101

kamilzielinskidev opened this issue Jul 28, 2022 · 8 comments

Comments

@kamilzielinskidev
Copy link

kamilzielinskidev commented Jul 28, 2022

Hi all,

I've briefly checked the docs and discord to check that and it sees that waitForNextUpdate is not available as return of renderHook for now in testing library.
Is there a plan to implement that or testing async hooks is considered as bad practice?

If it won't be implemented maybe theres an option to inform about it in the docs, as it seems to be main feature for testing async hooks

Thank you for what you do with the lib.

@eps1lon
Copy link
Member

eps1lon commented Jul 28, 2022

Is there a plan to implement that or testing async hooks is considered as bad practice?

the next update isn't necessarily relevant to the user. We want to discourage testing these implementation in Testing Library. waitFor has all the tools you need.

@eps1lon eps1lon closed this as not planned Won't fix, can't repro, duplicate, stale Jul 28, 2022
@srosato
Copy link

srosato commented Sep 15, 2023

@eps1lon is right on the waitFor, though since we are testing hooks, sometimes it might be harder to test what is relevent to a user since testing a hook is arguably in some way testing implementation details.

Here is an example of an upgrade I did (code simplified).

From:

import { renderHook } from '@testing-library/react-hooks';

describe(`Search users`, () => {
  let store: Store;

  const searchTerm = 'test';

  beforeEach(() => {
    store = createTestStore();
  });

  it(`searches for users`, async () => {
    const wrapper = <ReduxProvider store={store}>{children}</ReduxProvider>
    const { result, waitForNextUpdate } = renderHook(() => useSearchUsers(searchTerm), { wrapper });

    expect(result.current).toMatchObject({ users: [], isFetching: true });

    // OLD WAY
    await waitForNextUpdate();

    expect(result.current).toMatchObject({ users: [], isFetching: false });

    // assert more...
  });
});

To:

import { renderHook, waitFor } from '@testing-library/react';

describe(`Search users`, () => {
  let store: Store;

  const searchTerm = 'test';

  beforeEach(() => {
    store = createTestStore();
  });

  it(`searches for users`, async () => {
    const wrapper = <ReduxProvider store={store}>{children}</ReduxProvider>
    const { result } = renderHook(() => useSearchUsers(searchTerm), { wrapper });

    expect(result.current).toMatchObject({ users: [], isFetching: true });
    
    // NEW WAY
    await waitFor(() => expect(result.current).toMatchObject({ users: [], isFetching: false }));

    // assert more...
  });
});

@mirismaili
Copy link

mirismaili commented Oct 5, 2023

This is my previous test on Apollo's useQuery using waitForNextUpdate:

beforeAll(() => server.listen())
afterEach(() => server.resetHandlers())
afterAll(() => server.close())

test('useQuery', async () => {
  const wrapper = ({ children }) => <ApolloProvider client={createMockApolloClient()}>{children}</ApolloProvider>

  const { result, waitForNextUpdate } = renderHook(
    () =>
      useQuery(GET_ORGANIZATION_PACKS_API, {
        variables: { orgUid },
        fetchPolicy: 'no-cache',
        notifyOnNetworkStatusChange: true,
      }),
    { wrapper },
  )

  let { data, loading, error, refetch, networkStatus } = result.current
  expect(loading).toBeTrue()
  expect(networkStatus).toBe(NetworkStatus.loading)
  expect(error).toBeUndefined()
  expect(data).toBeUndefined()
  expect(refetch).toBeFunction()

  await waitForNextUpdate({ timeout: 5000 })
  ;({ data, loading, error, refetch, networkStatus } = result.current)

  expect(error).toBeUndefined()
  expect(loading).toBeFalse()
  expect(networkStatus).toBe(NetworkStatus.ready)
  expect(data).toMatchSnapshot({}, 'GET_ORGANIZATION_PACKS_API')
  expect(refetch).toBeFunction()
  refetch().then()

  await waitForNextUpdate()

  const data0 = data
  ;({ data, loading, error, refetch, networkStatus } = result.current)

  expect(error).toBeUndefined()
  expect(loading).toBeTrue()
  expect(networkStatus).toBe(NetworkStatus.refetch)
  expect(data).toEqual(data0)
  expect(refetch).toBeFunction()

  await waitForNextUpdate()
  ;({ data, loading, error, refetch, networkStatus } = result.current)

  expect(error).toBeUndefined()
  expect(loading).toBeFalse()
  expect(networkStatus).toBe(NetworkStatus.ready)
  expect(data).toEqual(data0)
  expect(refetch).toBeFunction()
})

const GET_ORGANIZATION_PACKS_API = gql`...`

It was working well.


Now, how do I write it w/o waitForNextUpdate?

@MatanBobi
Copy link
Member

MatanBobi commented Oct 29, 2023

@mirismaili you can use waitFor for that.

What about trying:

  waitFor(() => {
      expect(loading).toBeFalse()
  }, {timeout: 5000});
  waitFor(() => {
    expect(networkStatus).toBe(NetworkStatus.ready)
  }, {timeout: 5000});

That should work.

@likeuwill
Copy link

To mock apollo queries and make it work in test

  it('should return the mocked data', async () => {
    const { result } = setUp()

    await waitFor(() => {
      expect(result.current.loading).toBeFalsy()
    })

    await waitFor(() => {
      expect(result.current.data).toStrictEqual({
        recentlyViewed: [recentlyViewed]
      })
    })
  })

@elyobo
Copy link

elyobo commented Apr 2, 2024

waitFor + expect can break expect.assertions() expectations, as it may result in more assertions than expected due to retries

@githorse
Copy link

githorse commented Jul 6, 2024

I don't see how waitFor is a replacement for waitForNextUpdate at all. waitFor doesn't say anything about how many re-renders happened before I got there. The argument here seems to be that the internals of the hook and its behavior are a black box, and I should only test the dom. Nice idea, but it's critical that I understand and control how many render cycles are triggered by my hook. Is that possible under the new paradigm?

By way of example: I've just spent several days implementing some hooks based around Tanstack Query with selectors/transformations to ensure that consumers of the hook are only reloaded when their relevant slice of the underlying data is mutated (a la useContextSelector). It's a critical performance consideration for my app. How can I test that with this library?

@githorse
Copy link

Well, here's one way:

import {
  queries,
  Queries,
  renderHook,
  RenderHookOptions,
  RenderHookResult
} from '@testing-library/react'

export interface MyRenderHookResult<Result, Props> extends RenderHookResult<Result, Props> {
  renders: MutableRefObject<Result[]>
}

export function myRenderHook<
  Result,
  Props,
  Q extends Queries = typeof queries,
  Container extends Element | DocumentFragment = HTMLElement,
  BaseElement extends Element | DocumentFragment = Container,
>(
  render: (initialProps: Props) => Result,
  options?: RenderHookOptions<Props, Q, Container, BaseElement>,
): MyRenderHookResult<Result, Props> {

  const renders = {current: [] as Result[]}

  const _render = (props: Props) => {
    const returnValue = render(props)
    renders.current.push(returnValue)
    return returnValue
  }

  const result = renderHook(_render, options)

  return {
    ...result,
    renders
  }
}

...

test('it renders real nice', () => {
  const {result, renders} = myRenderHook(() => myHook())
  await waitFor(() => {
    expect(result.current).toEqual('ball python $49')
    expect(renders.current.length).toEqual(2)
  })
})

brian-smith-tcril added a commit to brian-smith-tcril/frontend-app-course-authoring that referenced this issue Mar 28, 2025
* Replaced `waitForNextUpdate` with `waitFor` (see testing-library/react-testing-library#1101)
* Updated `courseId` in success/fail tests to prevent concurrency issues
brian-smith-tcril added a commit to brian-smith-tcril/frontend-app-course-authoring that referenced this issue Mar 28, 2025
* Replaced `waitForNextUpdate` with `waitFor` (see testing-library/react-testing-library#1101)
* Updated `courseId` in success/fail tests to prevent concurrency issues
brian-smith-tcril added a commit to brian-smith-tcril/frontend-app-course-authoring that referenced this issue Mar 30, 2025
* Replaced `waitForNextUpdate` with `waitFor` (see testing-library/react-testing-library#1101)
* Updated `courseId` in success/fail tests to prevent concurrency issues
brian-smith-tcril added a commit to brian-smith-tcril/frontend-app-course-authoring that referenced this issue Mar 31, 2025
* Replaced `waitForNextUpdate` with `waitFor` (see testing-library/react-testing-library#1101)
* Updated `courseId` in success/fail tests to prevent concurrency issues
brian-smith-tcril added a commit to brian-smith-tcril/frontend-app-course-authoring that referenced this issue Apr 1, 2025
* Replaced `waitForNextUpdate` with `waitFor` (see testing-library/react-testing-library#1101)
* Updated `courseId` in success/fail tests to prevent concurrency issues
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants