如何在Jest中测试React Suspense中的’promise wrapper’,当通过axios获取数据时。

huangapple go评论61阅读模式
英文:

How to test a React Suspense 'promise wrapper' in Jest, when fetching data via axios

问题

以下是你要翻译的代码部分:

我正在尝试更新我的 useAxios 钩子以使其与 React Suspense 一起工作遵循此指南
https://deadsimplechat.com/blog/react-suspense/

const promiseWrapper = (promise: Promise<any>) => {
  let status = 'pending';
  let result: any;

  const suspender = promise.then(
    (value: any) => {
      status = 'success';
      result = value;
    },
    (error: any) => {
      status = 'error';
      result = error;
    }
  );

  return () => {
    switch (status) {
      case 'pending':
        throw suspender;
      case 'success':
        return result.data;
      case 'error':
        throw result;
      default:
        throw new Error('Unknown status');
    }
  };
};

export const useAxios = <T>(requestConfig: AxiosRequestConfig): T | null => {
  const [response, setResponse] = useState(null);
    
  useEffect(() => {
    const doRequest = () => {
      const result = axios.request(requestConfig);
      setResponse(promiseWrapper(result));
    };

    doRequest(requestConfig);
  }, []);

  return response;
};

这是你的代码的翻译部分,不包括问题或其他内容。如果需要进一步的帮助,请随时提出。

英文:

I'm trying to update my useAxios hook to work with React Suspense, following this guide:
https://deadsimplechat.com/blog/react-suspense/

const promiseWrapper = (promise: Promise&lt;any&gt;) =&gt; {
  let status = &#39;pending&#39;;
  let result: any;

  const suspender = promise.then(
    (value: any) =&gt; {
      status = &#39;success&#39;;
      result = value;
    },
    (error: any) =&gt; {
      status = &#39;error&#39;;
      result = error;
    }
  );

  return () =&gt; {
    switch (status) {
      case &#39;pending&#39;:
        throw suspender;
      case &#39;success&#39;:
        return result.data;
      case &#39;error&#39;:
        throw result;
      default:
        throw new Error(&#39;Unknown status&#39;);
    }
  };
};

export const useAxios = &lt;T,&gt;(requestConfig: AxiosRequestConfig): T | null =&gt; {
  const [response, setResponse] = useState(null);
    
  useEffect(() =&gt; {
    const doRequest = () =&gt; {
      const result = axios.request(requestConfig);
      setResponse(promiseWrapper(result));
    };

    doRequest(requestConfig);
  }, []);

  return response;
};

This 'promiseWrapper' pattern seems to be pretty common when using Suspense in react, with async calls.

I'm still wrapping my head around this, but here's my understanding of how it works...
We wrap our promise (the axios API call) in the promise wrapper, and then when the hook returns, it is returning the switch function that will return either return the data on success, throw the promise itself if its pending, and throws an error if there's an exception.

This all seems to work fine when I run the application, but I'm trying to write jest unit tests for when the useAxios hook returns an 'error' or is 'pending' and I cant get it to work.

For the error scenario, I have this jest test:

  it(&#39;should call endpoint and throw error on exception&#39;, async () =&gt; {
mockedAxios.request.mockRejectedValueOnce(new Error(&#39;error&#39;));

expect(() =&gt; {
  renderHook(() =&gt; useAxios&lt;TestResponseType&gt;({}))
}).toThrowError();
});

But the error is never thrown. And I'm not sure how to mock the pending response at all.

Any pointers on how I can get these test cases passing?

答案1

得分: 1

以下是您要翻译的内容:

Test the custom hook with a dumb component, the <Suspense/> component and a <ErrorBoundary/> component.

E.g.

import { ErrorBoundary } from "react-error-boundary";
import { useAxios } from "./useAxios";
import { render, screen } from "@testing-library/react";
import '@testing-library/jest-dom';
import axios from "axios";
import React, { Suspense } from "react";

describe('76404248', () => {
  test('should pass', async () => {
    const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => { })
    const requestSpy = jest.spyOn(axios, 'request').mockRejectedValueOnce(new Error('test error'))
    const TestComp = () => {
      useAxios({})
      return null;
    }
    render(
      <ErrorBoundary
        fallbackRender={({ error }) => <p>{error.message}</p>}
      >
        <Suspense fallback={<p>loading</p>}>
          <TestComp />
        </Suspense>
      </ErrorBoundary>
    )

    expect(await screen.findByText(/test error/i)).toBeInTheDocument();
    screen.debug();
    errorSpy.mockRestore();
    requestSpy.mockRestore();
  })
})

Test result:

 PASS  stackoverflow/76404248/useAxios.test.tsx (17.177 s)
  76404248
    ✓ should pass (165 ms)

  console.log
    <body>
      <div>
        <p>
          test error
        </p>
      </div>
    </body>

      at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:82:13)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |   83.33 |       50 |   85.71 |   82.61 |                   
 useAxios.ts |   83.33 |       50 |   85.71 |   82.61 | 10-11,24,28       
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        18.565 s, estimated 23 s

package versions:

"@testing-library/react": "^11.2.7",
"react-error-boundary": "^4.0.9",
"react": "^16.14.0",
英文:

Test the custom hook with a dumb component, the &lt;Suspense/&gt; component and a &lt;ErrorBoundary/&gt; component.

E.g.

import { ErrorBoundary } from &quot;react-error-boundary&quot;;
import { useAxios } from &quot;./useAxios&quot;;
import { render, screen } from &quot;@testing-library/react&quot;;
import &#39;@testing-library/jest-dom&#39;;
import axios from &quot;axios&quot;;
import React, { Suspense } from &quot;react&quot;;

describe(&#39;76404248&#39;, () =&gt; {
  test(&#39;should pass&#39;, async () =&gt; {
    const errorSpy = jest.spyOn(console, &#39;error&#39;).mockImplementation(() =&gt; { })
    const requestSpy = jest.spyOn(axios, &#39;request&#39;).mockRejectedValueOnce(new Error(&#39;test error&#39;))
    const TestComp = () =&gt; {
      useAxios({})
      return null;
    }
    render(
      &lt;ErrorBoundary
        fallbackRender={({ error }) =&gt; &lt;p&gt;{error.message}&lt;/p&gt;}
      &gt;
        &lt;Suspense fallback={&lt;p&gt;loading&lt;/p&gt;}&gt;
          &lt;TestComp /&gt;
        &lt;/Suspense&gt;
      &lt;/ErrorBoundary&gt;
    )

    expect(await screen.findByText(/test error/i)).toBeInTheDocument();
    screen.debug();
    errorSpy.mockRestore();
    requestSpy.mockRestore();
  })
})

Test result:

 PASS  stackoverflow/76404248/useAxios.test.tsx (17.177 s)
  76404248
    ✓ should pass (165 ms)

  console.log
    &lt;body&gt;
      &lt;div&gt;
        &lt;p&gt;
          test error
        &lt;/p&gt;
      &lt;/div&gt;
    &lt;/body&gt;

      at logDOM (node_modules/@testing-library/dom/dist/pretty-dom.js:82:13)

-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------|---------|----------|---------|---------|-------------------
All files    |   83.33 |       50 |   85.71 |   82.61 |                   
 useAxios.ts |   83.33 |       50 |   85.71 |   82.61 | 10-11,24,28       
-------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        18.565 s, estimated 23 s

package versions:

&quot;@testing-library/react&quot;: &quot;^11.2.7&quot;,
&quot;react-error-boundary&quot;: &quot;^4.0.9&quot;,
&quot;react&quot;: &quot;^16.14.0&quot;,

huangapple
  • 本文由 发表于 2023年6月5日 15:20:16
  • 转载请务必保留本文链接:https://go.coder-hub.com/76404248.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定