Search code examples
reactjsrtk-queryjest-fetch-mock

Testing Redux Toolkit Query using jest issue with auth


Im currently trying to write jest test for my RTKQuery, but I get stuck on the authentication level for the test.

Basically the api Im using is designed to have the token on query param instead of having it on the request header: "https://api/v1/something/meta/?token=userToken"

So when I try to test the api call it shows me the request has been rejected. Does anyone know how to write the test with this case?

here is my RTKQuery endpoint:

// index.ts
export const rootApi = createApi({
  reducerPath: "root",
  baseQuery: fetchBaseQuery({baseUrl: API_ROOT}),
  endpoints: () => ({});
})

// dataEndpoint.ts
const token = getToken(); // Gets the user's token from localStorage after user login

export cosnt apiWithData = rootApi.injectEndpoints({
  endpoints: (build) => ({
    fetchDataMetaList: build.mutation<DataType, any>({
      query: ({offset = 0, size = 20, body}) => ({
        // token is passed in for query param
        url: `${API_URL}?offset=${offset}&size=${size}&token=${token}`,
        method: "POST",
        body: body || {}
      })
    })
  })
})

below is my test:

// data.test.tsx
const body = { offset: 0, size: 20, body: {} };
const updateTimeout = 10000;

beforeEach((): void => {
  fetchMock.resetMocks();
})

const wrapper: React.FC = ({ children }) => {
  const storeRef = setupApiStore(rootApi);
  return <Provider store={storeRef.store}>{children}</Provider>
}

describe("useFetchDataMetaListMutation", () => {
  it("Success", async () => {
    fetchMock.mockResponse(JSON.string(response));
    cosnt { result, waitForNextupdate } = renderHook(
      () => useFetchDataMetaListMutation(), 
      { wrapper }
    )

    const [fetchDataMetaList, initialResponse] = result.current;
    expect(initialResponse.data).toBeUndefined();
    expect(initialResponse.isLoading).toBe(false);

    act(() => {
      void fetchDataMetaList(body);
    })

    const loadingResponse = result.current[1];
    expect(loadingResponse.data).toBeUndefined();
    expect(loadingResponse.isLoading).toBe(true);

    // Up til this point everything is passing fine
   
    await waitForNextUpdate({ timeout: updateTimeout });

    const loadedResponse = result.current[1];

    // expect loadedResponse.data to be defined, but returned undefined
    // console out put for loaded Response status is 'rejected' with 401 access level 
    // error code
  })
})

Solution

  • Doing a top-level const token means that as soon as that file is loaded, it will retrieve that token from the local store and that it will never be able to update that - so if that file is loaded before the user is logged in, it will be empty. That is pretty much also what happens in your test here.

    To be honest, this might be the first time ever that I see a token as part of the url (that is a serious security problem as the token would be shared between users on copy-pasting the url, it's visible in the browser history even after logout etc!).

    Unfortunately in that case, you cannot use prepareHeaders, but at least you could instead of the const use a function to get the current token - and if you import that from another file, you could also use jest mocking to just switch out that import.