簡體   English   中英

React-Query - 使用 react-testing-library 進行單元測試

[英]React-Query - Unit Test with react-testing-library

我有一個Dashboard組件,其中包含一個子組件,例如使用 react-query 的Child

我有一個Dashboard組件的現有單元測試開始失敗,錯誤是:

TypeError: queryClient.defaultQueryObserverOptions is not a function

  38 |     const { locale } = React.useContext(LocaleStateContext);
  39 |     const options = getOptions(locale);
> 40 |     return useQuery(
     |            ^
  41 |         rqKey,
  42 |         async () => {
  43 |             const result = await window.fetch(url, options);

測試片段:

const queryClient = new QueryClient();
const { getByTestId, getByRole } = render(
    <IntlProvider locale="en" messages={messages}>
        <QueryClientProvider client={queryClient}>
            <Dashboard />
        </QueryClientProvider>
    </IntlProvider>,
);

我閱讀了有關測試的文檔:

https://react-query.tanstack.com/guides/testing#our-first-test

但我不想一定要使用renderHook因為我對結果不感興趣。

編輯:

Child組件使用的是 function:

export function usePosts({ rqKey, url, extraConfig }: CallApiProps) {
    const { locale } = React.useContext(LocaleStateContext);
    const options = getOptions(locale);
    return useQuery(
        rqKey,
        async () => {
            const result = await window.fetch(url, options);
            const data = await result.json();
            return data;
        },
        extraConfig,
    );
}

這就是所謂的:

const { data, error, isFetching, isError } = usePosts({
        rqKey,
        url,
        extraConfig,
    });

根據您的回答,我應該創建一個單獨的 function :

async () => {
            const result = await window.fetch(url, options);
            const data = await result.json();
            return data;
        },

例如

export async function usePosts({ rqKey, url, extraConfig }: CallApiProps) {
    const { locale } = React.useContext(LocaleStateContext);
    const options = getOptions(locale);
    return useQuery(
        rqKey,
        await getFoos(url, options),
        extraConfig,
    );
}

然后在測試中模擬它。

如果我這樣做,我將如何訪問: error, isFetching, isError

因為usePosts()現在將返回一個Promise<QueryObserverResult<unknown, unknown>>

編輯2:

我嘗試簡化我的代碼:

export async function useFetch({ queryKey }: any) {
    const [_key, { url, options }] = queryKey;
    const res = await window.fetch(url, options);
    return await res.json();
}

然后用作:

const { isLoading, error, data, isError } = useQuery(
    [rqKey, { url, options }],
    useFetch,
    extraConfig,
);

所有作品。

Dashboard測試中,我然后執行以下操作:

import * as useFetch from ".";

jest.spyOn(useFetch, "useFetch").mockResolvedValue(["asdf", "asdf"]);

render(
        <IntlProvider locale="en" messages={messages}>
            <QueryClientProvider client={queryClient}>
                <Dashboard />
            </QueryClientProvider>
        </IntlProvider>,
    );

然后返回:

TypeError: queryClient.defaultQueryObserverOptions is not a function

      78 |     const { locale } = React.useContext(LocaleStateContext);
      79 |     const options = getOptions(locale);
    > 80 |     const { isLoading, error, data, isError } = useQuery(
         |                                                 ^
      81 |         [rqKey, { url, options }],
      82 |         useFetch,
      83 |         extraConfig,

您提到的文檔頁面解釋了如何測試依賴於 React Query 的自定義鈎子。 您是在使用基於 React Query 的自定義鈎子,還是只想測試使用 useQuery(React Query 提供的鈎子)的組件?

如果您只想測試使用 useQuery 的 Child ,您應該模擬您的“請求函數”(返回 Promises 的函數,用作 useQuery 的第二個arguments ),並在沒有任何提供程序的情況下渲染您的測試組件。

例如,在 Child 中說你有

const foo = useQuery('key', getFoos, { // additional config here });
// foo is a QueryResult object (https://react-query.tanstack.com/reference/useQuery)
// so your usePost function will return a QueryResult as well
// foo.data holds the query results (or undefined)
// you can access to foo.error, foo.isFetching, foo.status...
// also note that extra parameter to be passed to your async function 
// should be part of the request key. Key should be an array :
// useQuery(['key', params], getFoos, { // additional config });
// so params object props will be passed as parameters for getFoos fucntion
// see https://react-query.tanstack.com/guides/query-keys#array-keys

...並且 getFoos 在path/to/file/defining/getFoos.ts中定義為

const getFoos = async (): Promise<string[]> => await fetch(...);

...然后在 Child.test.tsx 你可以做

import * as FooModule from 'path/to/file/defining/getFoos';

// this line could be at the top of file or in a particular test()
jest.spyOn(FooModule, 'getFoos').mockResolvedValue(['mocked', 'foos']);

// now in your Child tests you'll always get ['mocked', 'foos']
// through useQuery (in foo.data), but you'll still have to use https://testing-library.com/docs/dom-testing-library/api-async/#waitfor (mocked but still async)
// No need for QueryClientProvider in this case, just render <Child />

回答:

盡管上面的答案幫助我朝着正確的方向前進,但根本問題是我使用 mockImplementation 提供上下文,然后使 QueryClientProvider 給出的上下文變得無用,例如

jest.spyOn(React, "useContext").mockImplementation(() => ({
    ...
}));

我最終刪除了 mockImplementation 並在我的 UserStateContext.Provider 中添加了 QueryClientProvider 並解決了問題:

render(
    <IntlProvider locale="en" messages={messages}>
        <UserStateContext.Provider value={value}>
            <QueryClientProvider client={queryClient}>
                <Dashboard />
            </QueryClientProvider>
        </UserStateContext.Provider>
    </IntlProvider>,
);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM