Giter VIP home page Giter VIP logo

Comments (65)

bonnie avatar bonnie commented on August 17, 2024 4

This is fantastic -- thank you, Dominik!

from blog-comments.

andrewluetgers avatar andrewluetgers commented on August 17, 2024 1

This is exactly what I was looking for, thank you sir. Solid series all around, well done!

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

@mauriciosoares I would appreciate a PR to the docs, maybe a small paragraph and an updated example here :)

from blog-comments.

mauriciosoares avatar mauriciosoares commented on August 17, 2024 1

@TkDodo I opened a PR for that TanStack/query#2460 🀘

I used pretty much the same info from that blog post, I don't see a reason to rephrase that, since it already pretty clear and concise

from blog-comments.

janusch avatar janusch commented on August 17, 2024 1

Makes sense, thanks a lot!
I will try out either solution and decide if it makes more sense to have the useQuery in each getCached helper or if I will setQueryData for the tests.

Looking forward to read the rest of your react-query blog series.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

@eoghanmccarthy I think you'd need to pass them all in again, so like in your first way. But that seems more like a question on how rerender from testing-library works.

from blog-comments.

Liam-Tait avatar Liam-Tait commented on August 17, 2024 1

Thank you, this was really useful

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

@eoghanmccarthy if you use mutateAsync, you always have to catch errors manually. This is also in the docs. I prefer to just call mutate and work with the provided onSuccess and onError callbacks.

from blog-comments.

cloud-walker avatar cloud-walker commented on August 17, 2024 1

Thanks, I have seen the example repo. As far as I can tell I'm doing everything the same, but even this isn't returning me anything:

import { setupServer } from 'msw/node';
import { rest } from 'msw';

const handlers = [
  rest.get('*', (req, res, ctx) =>
    res(
      ctx.status(200),
      ctx.json({ working: 'yes please'})
    )
  )
]

const server = setupServer(...handlers);

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

describe('a passing test', () => {
  it('passes', () => {
    const res = await fetch('/1099');
    if (res.ok) console.log(res.json())
  });
});

Output of console.log is Promise { }. I would expect to see { working: 'yes please' }

hi @drmeloy, res.json() returns a Promise! You need to await it ☺️

from blog-comments.

bsides avatar bsides commented on August 17, 2024 1

Hi Tk, thanks for the great post. I have a question here about react-query mutation, testing and react-router history push after a mutation.

The question is: how to test a location change with state after a react-query mutation?
Short example:

function SomeComponent() {
  const history = useHistory()
  const location = useLocation()
  const mutation = useMutation(somefetch, {
    onSuccess: (data) => history.push(`/someurl/${data.id}`, { THIS_DATA_HERE: 'SOMETHING' })
  })

  return (
    <>
      {location?.state ? <p>id: {location.state.THIS_DATA_HERE}</p> : null}
      <button type="button" onClick={() => mutation.mutate({something: 'somedata'})}>create</button>
    </>
  )
}

If I test the route itself it goes as expected, the user just go there and the data is refreshed via tests. But if I send anything via history state, it doesn't get updated in the test for whatever reason. I don't know if it's a problem with jest, testing-library, react-router/history or react-query.

I also have made a codesandbox with a minimum test, showing it doesn't ever update the state from location (but in the browser it works as expected):
https://codesandbox.io/s/cool-goldstine-4fizo?file=/src/App.test.js

Thank you!

from blog-comments.

72gm avatar 72gm commented on August 17, 2024 1

interesting article, though your tests don't actually compile... "Binding element 'children' implicitly has an 'any' type.ts(7031)"

from blog-comments.

bsides avatar bsides commented on August 17, 2024 1

@bsides I've re-added react-query mutations to your sandbox and it seems to work fine: https://codesandbox.io/s/friendly-brahmagupta-1dz7v?file=/src/App.test.js

maybe you can add more code until it breaks again? maybe you removed something else, too? Maybe it's because of the actual network request or so, because I've just used your promise stub for now ...

Nice, thanks for doing that. I'll try to remove everything and make a cleaner project, looks like react-query isn't the thing to be checked here, I'm more suspicious about react-router. It was just weird that removing react-query in the first example made it work. Oh well.

from blog-comments.

geekashu avatar geekashu commented on August 17, 2024 1

Thanks a lot man. It helped a lot. I made some changes to the custom hook and now using isSuccess and data flag to perform the business logic. This way it has become a lot easier to mock the hook and test the component.

from blog-comments.

72gm avatar 72gm commented on August 17, 2024 1

@72gm it compiles fine for me πŸ€”

Yeah your repo compiles fine. I'd just tried stealing a couple of your code snippets, which didn't!

Complicated stuff getting tests to work that use React-Query! Although it didn't help I was doing a couple of foolish things!

Thank goodness you added this repo & blog... good shout on the MSW library too

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

@vkostunica no, I don't think so. Try to mock as little as possible. If you provide a dehydratedState, all the Hydrate component does is put that state into the queryClient ...

from blog-comments.

vkostunica avatar vkostunica commented on August 17, 2024 1

Idea behind this is just to support both render and rerender for components, right? I usually saw people to use wrapper option from render to wrap with provider.

export function renderWithClient(ui: React.ReactElement) {
    const testQueryClient = createTestQueryClient()
    const { rerender, ...result } = render(
        <QueryClientProvider client={testQueryClient}>{ui}</QueryClientProvider>
    )
    return {
        ...result,
        rerender: (rerenderUi: React.ReactElement) =>
            rerender(
                <QueryClientProvider client={testQueryClient}>{rerenderUi}</QueryClientProvider>
            ),
    }
}

Like this:

https://github.com/leerob/daydrink/blob/master/test/test-utils.js

const customRender = (ui, options) =>
    render(ui, {
        wrapper: ChakraRenderer,
        ...options
    });

And for wrapper props:

testing-library/react-testing-library#780 (comment)

from blog-comments.

vkostunica avatar vkostunica commented on August 17, 2024 1

Yes, it does.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

I don't want to redirect in onSuccess handler for every mutation.

what's the difference between:

  onClick={async () => {
    await deletePost(post.id); // block redirect bellow until mutation is not resolved
    await router.push(Routes.SITE.HOME);
  }}

and:

  onClick={() => {
    deletePost(post.id, { onSuccess: () => router.push(Routes.SITE.HOME) })
  }}

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024 1

@nemanjam I would stick to what hooks-testing-library says about when to use / when not to use it:

When to use this library
You're writing a library with one or more custom hooks that are not directly tied to a component
You have a complex hook that is difficult to test through component interactions
When not to use this library
Your hook is defined alongside a component and is only used there
Your hook is easy to test by just testing the components using it

so, in your case, the hook is not overly complex, and its also likely easy to test together with your component (e.g. that it's disabled initially and then enables after some user interaction), so would go with that.

I'm merely showing that you can test custom hooks with that approach, not necessarily that you should :)

from blog-comments.

Mmferry avatar Mmferry commented on August 17, 2024

Thanks a lot Dominik
Can you take a look on that error please
https://stackoverflow.com/questions/67354617/typeerror-reactquery-default-is-not-a-constructor

from blog-comments.

cloud-walker avatar cloud-walker commented on August 17, 2024

didn't know about the silence errors thing, very nice!

from blog-comments.

mauriciosoares avatar mauriciosoares commented on August 17, 2024

The Turn off retries tip should be in the official documentation. I spent hours trying to figure out why my query wasn't resolving using jest + enzyme, to only remember that the QueryClient has the retries configured to 3 by default...

from blog-comments.

seafoam6 avatar seafoam6 commented on August 17, 2024

Great post. A section on how to test useMutation would also be appreciated.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

Great post. A section on how to test useMutation would also be appreciated.

Thanks, I'll see what I can do :)

from blog-comments.

janusch avatar janusch commented on August 17, 2024

@TkDodo thank you for the great article!

I am wondering how you would go about testing queryClient.getQueryData() calls. In my app I load some data on startup and then just use it from the query-cache. I would like to test components and functions that use above function.

How could I load some data into the cache?

I tried just calling the custom hook that gets the intial data in the test, but it ends up in the wrong query client. Can you think of a way on how to re-use the query client in the setup with the wrappers that you outlined above?

Would love to hear what you think about this use case for testing with the cached data.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

I am wondering how you would go about testing queryClient.getQueryData() calls. In my app I load some data on startup and then just use it from the query-cache. I would like to test components and functions that use above function.

So I wouldn't do that, because queryClient.getQueryData() doesn't create a subscription to the queryCache. So if new data comes in, that component won't re-render on itself - only when a parent re-renders. I always advocate for: if you need data, just call useQuery, or better yet a custom hook that wraps useQuery. It will make sure your component works in isolation, not just if it's a child of a specific parent. This issue actually becomes apparent when you try to test it, because it can't "work alone".
If you are worried about too many background updates, you can set a staleTime on that query. As long as the query is fresh, data will only come from the cache with useQuery. Otherwise, you can also prop-drill data down, which would make it explicit and also easier to test, because now it's just a prop.

How could I load some data into the cache?

If you really want that, you can just call queryClient.setQueryData() after you've created the queryClient to prime the cache.

from blog-comments.

janusch avatar janusch commented on August 17, 2024

@TkDodo Thank you for the feedback!
That is a really good point. The thing is that the data I am loading is kind of configuration/master data that does not change much at all. And where I was first getting the data from different endpoints I ended up bundling it all into one configuration data endpoint that gives me all the different pieces. And many components just need one specific field of that config data. That is when I started creating those getCached* helpers that use getQueryData() and just return a specific field of the configuration data cache.

Using the wrappers from your examples how would I go about adding data for a specific test with queryClient.setQueryData()?

How can I get access to the same instance of the query client inside the test?

Thank you again for your guidance on this!

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

choosing the createWrapper approach, I think you can do this:

const createWrapper = (someKey, someInitialData) => {
  const queryClient = new QueryClient()
  queryClient.setQueryData(someKey, someInitialData)

  return ({ children }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  )
}

from blog-comments.

eoghanmccarthy avatar eoghanmccarthy commented on August 17, 2024

Hi, thanks for answering earlier questions on twitter. I just had one more. If I need to include other providers in renderWithClient do I declare them also in the return statement?

Like this:

export function renderWithClient(ui) {
  const testQueryClient = createTestQueryClient();
  const { rerender, ...result } = render(
    <QueryClientProvider client={testQueryClient}>
      <Provider store={mockedStore} context={ReactReduxContext}>
        <Router history={mockMemoryHistory}>
          <AuthProvider>{ui}</AuthProvider>
        </Router>
      </Provider>
    </QueryClientProvider>
  );
  return {
    ...result,
    rerender: rerenderUi =>
      rerender(
        <QueryClientProvider client={testQueryClient}>
          <Provider store={mockedStore} context={ReactReduxContext}>
            <Router history={mockMemoryHistory}>
              <AuthProvider>{rerenderUi}</AuthProvider>
            </Router>
          </Provider>
        </QueryClientProvider>
    )
  };
}

Or not, so like this?

export function renderWithClient(ui) {
  const testQueryClient = createTestQueryClient();
  const { rerender, ...result } = render(
    <QueryClientProvider client={testQueryClient}>
      <Provider store={mockedStore} context={ReactReduxContext}>
        <Router history={mockMemoryHistory}>
          <AuthProvider>{ui}</AuthProvider>
        </Router>
      </Provider>
    </QueryClientProvider>
  );
  return {
    ...result,
    rerender: rerenderUi =>
      rerender(<QueryClientProvider client={testQueryClient}>{rerenderUi}</QueryClientProvider>)
  };
}

from blog-comments.

drmeloy avatar drmeloy commented on August 17, 2024

I've tried following this but it does not seem like my MSW is even running. Is there a way to confirm that the MSW is running properly? I'm not getting any errors or anything, and when I console.log my result my result.data is undefined. As far as I can tell I've followed your implementation to a T

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@drmeloy have you seen the example repo? It has working tests, so you can clone it and work from there if you want :)
You can also reach out to msw on twitter: https://twitter.com/apimocking

from blog-comments.

drmeloy avatar drmeloy commented on August 17, 2024

Thanks, I have seen the example repo. As far as I can tell I'm doing everything the same, but even this isn't returning me anything:

import { setupServer } from 'msw/node';
import { rest } from 'msw';

const handlers = [
  rest.get('*', (req, res, ctx) =>
    res(
      ctx.status(200),
      ctx.json({ working: 'yes please'})
    )
  )
]

const server = setupServer(...handlers);

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

describe('a passing test', () => {
  it('passes', () => {
    const res = await fetch('/1099');
    if (res.ok) console.log(res.json())
  });
});

Output of console.log is Promise { }. I would expect to see { working: 'yes please' }

from blog-comments.

drmeloy avatar drmeloy commented on August 17, 2024

Oh and my setupTests.ts doesn't hold anything, just

import '@testing-library/jest-dom';

When I commented that out just for a sanity check nothing changed

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

Not sure if that’s just a typo, but you can only await in async functions. I can take a look at a codesandbox reproduce if you create one

from blog-comments.

eoghanmccarthy avatar eoghanmccarthy commented on August 17, 2024

Wondering has anyone got a useMutation status 500 test working? useMutation status 200 tests are good but the tests for 500 status are causing the test to fail.

from blog-comments.

eoghanmccarthy avatar eoghanmccarthy commented on August 17, 2024

On the question above here is the hook and test:

export const useCreateSomething = () => {
  return useMutation(async () => {
    const { data } = await axios.post(`/something`);
    return data;
  });
};
describe('query hooks', () => {
  test('test 500 status', async () => {
    server.use(
      rest.post(`/something`, (req, res, ctx) => {
        return res(ctx.status(500));
      })
    );

    const { result, waitFor } = renderHook(() => useCreateSomething(), {
      wrapper: createWrapper()
    });

    const { mutateAsync } = result.current;

    act(() => {
      mutateAsync();
    });

    await waitFor(() => result.current.isError);
    expect(result.current.error).toBeDefined();
  });
});

Looks like the expected values are resolved but the test still fails. Same pattern works good for testing errors with useQuery but useMutation has this issue.

from blog-comments.

eoghanmccarthy avatar eoghanmccarthy commented on August 17, 2024

Seems like you have to catch the error to make the test pass. This works:

const mockOnError = jest.fn();

act(() => {
      mutateAsync().catch(err => {
        mockOnError(err);
      });
});

await waitFor(() => result.current.isError);
expect(result.current.error).toBeDefined();

Issue is only with useMutation not useQuery

from blog-comments.

Justinohallo avatar Justinohallo commented on August 17, 2024

Hi @TkDodo - do you have insights into the value of these tests ?

Does it make sense to write a test for every single one of your hooks? Or should you be testing the component that calls the hook ?

My company maintains a repo specifically for sharing hooks between multiple projects. How valuable is it to test every single hook in that repo vs simply testing the components across the repos that use that hook ?

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@Justinohallo the granularity of what you want to test is, of course, up to you. I do like integration tests most, testing complete pages with cypress.

if you have a library that exposes hooks, I think it would be good if that library would make sure that its hooks are working as intended. For example, react-query also has tests internally for hooks.

from blog-comments.

Meenakshi-SR avatar Meenakshi-SR commented on August 17, 2024

Wondering has anyone got on how to test useMutation's onError and onSuccess ?

const useSubmitzform = ({formdata}) =>{
    const {mutate}  = useMutation((payload)=>{
       //axios call
    },
    { 
        onError: (error)=>{
            console.log(error);
        },
        onSuccess: (data)=>{
            console.log(data);
        },
}

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@Meenakshi-SR I think it depends on what you want to test by that. Just testing if the functions have been invoked doesn't sound too appealing, because that would be testing react-query internals. We do test this, e.g. here

If you show for example a custom toast notification, it might make sense to render the hook in a component and assert that the notification is really visible on the screen. If it's just logging, mocking the log function with jest and asserting that it has been called could work.

from blog-comments.

OriAmir avatar OriAmir commented on August 17, 2024

Really thanks for the really great articles!
few questions:

  1. How we could test use mutation hooks ? (like we test the use query hooks)
  2. Do you think it's correct to set all application handlers in the same file (handlers.ts) and then initialize every test with the server using the setupTest.ts file with all that handlers? because other component we tests not using sometimes the server at all.

another note: the waitFor dosent work for me as youe example and I have to set:
await waitFor(() => expect(result.current.isSuccess).toBeTruthy());

Thanks again!

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

How we could test use mutation hooks ? (like we test the use query hooks)

I think I'll have to add that to the list of things to write about. I haven't thought much about it, but I would likely do it similar to how it's done with queries - except that you'd need to interact with a button or so first to trigger the mutation. Are you struggling with something specifically?

Do you think it's correct to set all application handlers in the same file (handlers.ts) and then initialize every test with the server using the setupTest.ts file with all that handlers? because other component we tests not using sometimes the server at all.

Actually, no. I would rather set this up on a per-test level because each test might need different response data like I'm doing it here. It doesn't hurt to set some global handlers if they fit most of your cases though.

another note: the waitFor dosent work for me as youe example

interesting - it works fine in the repo I linked ...

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

interesting. according to this article, what you did looks correct. I don't really see why this isn't working, but I also don't think that it's a react-query issue. You can try to remove more things like react-query to narrow it down.

from blog-comments.

bsides avatar bsides commented on August 17, 2024

interesting. according to this article, what you did looks correct. I don't really see why this isn't working, but I also don't think that it's a react-query issue. You can try to remove more things like react-query to narrow it down.

Thanks for the reply @TkDodo

I also made a repo with v6 of react-router, there are some changes in the API and I hoped that would be some bug there, but at this point I pretty much doubt it:

https://codesandbox.io/s/thirsty-wind-duv4m?file=/src/App.tsx

I will do another one without react-query like you suggested to check one by one.

from blog-comments.

bsides avatar bsides commented on August 17, 2024

I went in a long research for this but it looks like something about react-query ins't right. If I remove it completely and even simulate a promise in the button click and redirect after, the test will pass.

Made another version here: https://codesandbox.io/s/qdoie?file=/src/App.tsx

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@72gm it compiles fine for me πŸ€”

from blog-comments.

nazmeln avatar nazmeln commented on August 17, 2024

How to test properly useInfiniteQuery hook, especially when we have getNextPageParam?

      getNextPageParam: (lastPage, allPages) => {
        if (lastPage.length < DEFAULT_PAGE_LIMIT) {
          return undefined;
        }

        return {
          offset: allPages.flat().length
        };
      },

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@bsides I've re-added react-query mutations to your sandbox and it seems to work fine: https://codesandbox.io/s/friendly-brahmagupta-1dz7v?file=/src/App.test.js

maybe you can add more code until it breaks again? maybe you removed something else, too? Maybe it's because of the actual network request or so, because I've just used your promise stub for now ...

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@nazmeln if you mock the request on network layer with nock or msw, you can just return the same thing your real backend would return and the query should run as if it were in production, including getNextPageParam etc.

from blog-comments.

geekashu avatar geekashu commented on August 17, 2024

I have a Login component, trimmed version is like this -
The password input element is only shown after the useUserInfo has run returned basic as the hook output.

const Login = (props) => {
  const [showPasswordField, setShowPasswordField] = useState(false);
  const { userInfoQuery } = useUserInfo({
    username: loginParams.username,
    onSuccess: (res) => {
      if (res.authSource !== 'basic') {
       // Redirect to a new page
      } else {
        showPasswordField(true);
      }
    },
    onError: (err) => {
      showPasswordField(false);
    }
  });

  // Conditional Login form

useUserInfo.hook.js

  export const useUserInfo = (config) => {
  const { userInfo } = useUserSession();
  const { username, onSuccess, onError } = config;
  const { refetch: userInfoQuery } = useQuery(
    ["userInfoQuery", username],
    () => userInfo(username),
    {
      enabled: false,
      onSuccess,
      onError
    });

  return { userInfoQuery };
};

Now, I am trying to test the conditional render of the password input field, but it doesn't seems to be rendering. onSuccess call is not working in the test.

Login.test.js

  it("test conditional render if authSource is basic", async () => {

    const userInfoQuery = jest.fn();
    useUserInfo.mockImplementation((config) => ({
      userInfoQuery,
      data: {
          authSource: "basic"
      }
    }));

    const wrapper = renderWithTheme();

    await act(async () => {
      await inputFieldUsername.simulate("change", { target: { value: "[email protected]" } });
      await submitButton.simulate("submit");
    });
    expect(userInfoQuery).toHaveBeenCalledTimes(1);

    wrapper.update();

    await act(async () => {
      // After user has clicked on initial submit button(and has hit the useUserInfo hook), password field should become visible.
      const inputFieldPasswordT2 = await wrapper.find("input#password");
      console.log(inputFieldPasswordT2.debug());
      expect(inputFieldPasswordT2.exists()).toBe(true);
    });

I think, something is not right with my mockImplementation. I would appreciate it if anyone could point me in the right direction.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024
  1. I think login should always be a mutation: https://twitter.com/TkDodo/status/1483157923572457476
  2. you don't need state to compute the visibility of your field:
const query = useQuery()
const showPasswordField = query.isSuccess && query.data.authSource === 'basic'

not only is deriving that state much simpler and easier to grasp, it's also "safer" because onSuccess is tied to the queryFn being run, and if you use staleTime to retrieve cached data, you might get out of sync (not specific to your case, just a general remark).

from blog-comments.

geekashu avatar geekashu commented on August 17, 2024

The final submission is going to be a Mutation only. It is just some initial checks to know who is the user and based on that what mutation needs to be called.

from blog-comments.

geekashu avatar geekashu commented on August 17, 2024

Is there no way we can mock the onSuccess ? There are some more business logic that happens in onSuccess call.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

if you create a complete mock for your custom hook (userInfoQuery), the react-query useQuery hook is never executed, which is likely what you want (otherwise you wouldn't have done the mock), so onSuccess is also never called. You would have to mock useQuery instead, but as the article states, I think its best to mock the network layer and execute all the code just like you would in production without mocking anything in code.

from blog-comments.

iamravisingh avatar iamravisingh commented on August 17, 2024

@TkDodo I am trying to mock my customQuery hook but facing some overload issue
i.e useGet
const useGetMock = jest.spyOn('axios', 'get').mockImplementation(() => mockSuccess );
below is the method defined

  endpoint: string,
  headers: Record<string, string>,
  opts?: queryOptions
) : UseQueryResult<string> {
  return useQuery(
    ['get', endpoint, headers || ''],
    () => apiClient(endpoint, Method.GET, headers),
    opts
  );
}

test-case

    //Arrange
    const { result } = renderHook(() => useGet('https://run.mocky.io/v3/f042917b-d3a7-41ec-acb2-7b25a37326b2', {}), {
      wrapper: createWrapper(),
    });
    const useGetMock = jest.spyOn(useGet, 'fetch').mockImplementation(() =>  Promise.resolve(mockSuccess)  );
    const { container } = render(
      <ThemeProvider theme={jiffy}>
        <QueryClientProvider client={queryClient}>
          <ClientList id="1" />
        </QueryClientProvider>
      </ThemeProvider>
    );
    screen.debug();
    //Act
    const clientList = screen.getByTestId("client-list")

    //Assert
    expect(useGetMock).toHaveBeenCalled();
    expect(clientList).toBeInTheDocument()
  });

error :

  Overload 1 of 4, '(object: (endpoint: string, headers: Record<string, string>, opts?: queryOptions) => UseQueryResult<string, unknown>, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'.
  Overload 2 of 4, '(object: (endpoint: string, headers: Record<string, string>, opts?: queryOptions) => UseQueryResult<string, unknown>, method: never): SpyInstance<never, never>', gave the following error.
    Argument of type 'string' is not assignable to parameter of type 'never'```

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@iamravisingh can't tell from just that code snippet, sorry. Again, my advice is to not spy on methods, but mock the network request itself. I can take a look if you have a codesandbox reproduction that shows the error.

from blog-comments.

vkostunica avatar vkostunica commented on August 17, 2024

What about Hydrate provider that is used with Next.js and SSR, should it's state be mocked in some way too?

import { Hydrate, QueryClient, QueryClientProvider } from 'react-query';

<QueryClientProvider client={queryClient}>
	<Hydrate state={dehydratedState}>
		<Component {...pageProps} />
	</Hydrate>
	<ReactQueryDevtools />
</QueryClientProvider>

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

does wrapper also handle rerender ? I didn't know that ...

from blog-comments.

vkostunica avatar vkostunica commented on August 17, 2024

@eoghanmccarthy if you use mutateAsync, you always have to catch errors manually. This is also in the docs. I prefer to just call mutate and work with the provided onSuccess and onError callbacks.

What if I want to block redirect bellow until mutation is not resolved, regular mutate is sync and returns void? I don't want to handle mutation error on another page. I don't want to redirect in onSuccess handler for every mutation.

const { mutateAsync: deletePost, ...restDelete } = useDeletePost();

...

 <Button
  variant="secondary"
  onClick={async () => {
    await deletePost(post.id); // block redirect bellow until mutation is not resolved
    await router.push(Routes.SITE.HOME);
  }}
  >
  {!restDelete.isLoading || isFetching ? 'Delete' : 'Deleting...'}
</Button>

from blog-comments.

vkostunica avatar vkostunica commented on August 17, 2024

I don't want to redirect in onSuccess handler for every mutation.

what's the difference between:

  onClick={async () => {
    await deletePost(post.id); // block redirect bellow until mutation is not resolved
    await router.push(Routes.SITE.HOME);
  }}

and:

  onClick={() => {
    deletePost(post.id, { onSuccess: () => router.push(Routes.SITE.HOME) })
  }}

So I can pass onSuccess inline as option, great then, thank you.

from blog-comments.

nemanjam avatar nemanjam commented on August 17, 2024

Very clean example, I am using it in my project. Although one thing confuses me, which is what actual logic/code is being tested here?

In your example there is literally no custom logic to be tested, libraries do all the work.

const fetchRepoData = (): Promise<{ name: string }> =>
    axios
        .get('https://api.github.com/repos/tannerlinsley/react-query')
        .then((response) => response.data)

export function useRepoData() {
    return useQuery('repoData', fetchRepoData)
}

If I use one random, more realistic hook from my project, there is also almost no custom logic. How such tests add any value to the project? What logic is aimed to be tested?

const getUser = async (params: GetUserQueryParams) => {
  const { data } = await axiosInstance.get<ClientUser>(Routes.API.PROFILE, { params });
  return data;
};

export const useUser = (params: GetUserQueryParams) => {
  const subKey = params?.username || params?.email || params?.id;

  const query = useQuery<ClientUser, AxiosError>(
    filterEmpty([QueryKeys.USER, subKey]),
    () => getUser(params),
    { enabled: !!subKey }
  );
  return query;
};

from blog-comments.

apasternack avatar apasternack commented on August 17, 2024

Is there a good example of a container/smart component using useQuery?

When I test the basic example component here: https://react-query.tanstack.com/overview

mocked out with data using msw, the markup does not load.

If I do the same API call with the native react useEffect hook instead, the markup appears and can be used to find elements and expect values on.

from blog-comments.

TkDodo avatar TkDodo commented on August 17, 2024

@apasternack I think I have an example for that in the linked repo:

https://github.com/TkDodo/testing-react-query/blob/ce2a488291754a2f773ebddd18d5d924458ebfc6/src/tests/App.test.tsx#L8-L12

from blog-comments.

apasternack avatar apasternack commented on August 17, 2024

@TkDodo Thanks that helped, got my test working following that pattern

I still don't understand why when you test a container/smart component that directly uses useQuery the markup of the component under test never loads. This issue goes away when wrapping useQuery in a custom hook, but I am unclear on why that is the case.

Thanks!

from blog-comments.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.