0

I have the following hook:


function useWelcomeMessage() {
  const { data, error} = useGetWelcomeMessage();

  const printMessage = useCallback(async () => {
    if (data) {
        alert(data)
    }
    
    if (error) {
      throw new Error(error);
    }
  }, [data, error]);

  return { printMessage };
}

export default useWelcomeMessage;

I'm trying to write a test for it using vitest to make sure that alert(data).

const mockMessage = 'hello world';

const server = setupServer(
  http.get('*/api/v1/getMessage', () => {
    return HttpResponse.text(mockMessage);
  })
);

it('calls alert with the data', async () => {
    const { result } = renderHook(() => useWelcomeMessage(), {
      wrapper: ({ children }) => <Provider store={store}>{children}</Provider>,
    });
    

    await act(async () => {
      await result.current.printMessage();
    });

    expect(alert).toHaveBeenCalledWith(mockMessage);
});

My problem is, that when I invoke printMessage, the useGetWelcomeMessage hasn't had time to execute yet, and thus data is still undefined.

I'm trying to figure out the best way to wait for the RTK Query call to have completed. I tried fake timers and advancing those to no effect. A solution that did work, was having a vi.fn() in the MSW get handler, and waiting on that to have been called before proceeding with the test.

1 Answer 1

0

I assume you have hook more or less like below:

import { useCallback } from "react";
import { useGetWelcomeMessage } from "./useGetWelcomeMessage";

function useWelcomeMessage() {
    const { data, error } = useGetWelcomeMessage();
  
    const printMessage = useCallback(async () => {
      if (data) {
          alert(data)
      }
      
      if (error) {
        throw new Error(error);
      }
    }, [data, error]);
  
    return { printMessage };
  }
  
export default useWelcomeMessage;

The important part is

import { useGetWelcomeMessage } from "./useGetWelcomeMessage";

which will allow us to mock this dependency, as it should be mocked, not use a real thing.

In unit test we should mock everything except for the code being tested, in our case the useWelcomeMessage hook.

So, your test should look like this (and as I checked, it passes correctly):

import { renderHook, act } from '@testing-library/react';
import { describe, expect, it, vi } from 'vitest';
import useWelcomeMessage from './useWelcomeMessage';
import * as useGetWelcomeMessageModule from './useGetWelcomeMessage';

global.alert = vi.fn();

describe('useWelcomeMessage', () => {
  it('calls alert with the data', async () => {
    const expectedData = 'hello world'

    vi.spyOn(useGetWelcomeMessageModule, 'useGetWelcomeMessage').mockReturnValue({
      data: expectedData,
      error: '',
    });

    const { result } = renderHook(() => useWelcomeMessage());

    await act(async () => {
      await result.current.printMessage();
    });

    expect(alert).toHaveBeenCalledWith(expectedData);
  });
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your reply Michal. The recommendation from RTK query maintainers is to mock out the underlying network request intead of the RTK Query provided hook itself, which is why i was hoping to accomplish it in my original approach.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.