component test 問題集4(React18 + TS + Jest + react-testing-library)


Posted by TempuraEngineer on 2023-01-07

目錄

useNavigate()有無被呼叫

重點不是react-router-dom是否正常運作,而是useNavigate(或用到它的event handler)是否被呼叫,點擊按鈕時path是否改變則交給E2E測試

需要mock react-router-dom的custom function

const mockUseNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
  useNavigate: () => mockUsedNavigate,
  useMatch: () => mockUseMatch,
}));

react jest mock useNavigate()


router的path有無改變

這會需要建立router context

const router = createBrowserRouter({
    // ...
});

const App = () => {
    return (
        <div>
            <RouterProvider router={router}>
                // ...
            </RouterProvider>
        </div>
    );
}

但如果未來不用react-router-dom做router、router結構差太多的話可能就要整個拆掉,所以建議用E2E來測

useNavigate() may be used only in the context of a Router component

testing library - React Router


測試router的邏輯是否要建立router context

例如不同權限的使用者能看到的router不同

不用,但需要做一個類似專案結構的router,並把他傳給要測試其邏輯的函式


Mui的Drawer render出來一片空

從Mui提供的範例中可以發現drawer被關掉的時候是不存在DOM上的,打開的時候才會被插入body內

再來跟據官方文件中關於container、baseElement的部分
這兩者都是由 react-testing-library 的 render function 所回傳,且都用於 query DOM

container

By default, React Testing Library will create a div and append that div to the document.body and this is where your React component will be rendered.
If you provide your own HTMLElement container via this option, it will not be appended to the document.body automatically.


baseElement

If the container is specified, then this defaults to that, otherwise this defaults to document.body.
Note: the queries returned by the render looks into baseElement, so you can use queries to test your portal component without the baseElement.

使用 render()時沒有特別設定 option.container 的話,container會是建立一個div然後把渲染的組件放進去

const { container } = rende(<p>this is NOT in Portal</p>);

console.log(container.outerHTML); 

// <div>
//    <p>this is NOT in Portal</p>
// </div>

一樣是沒有特別設定 option.container,baseElement會是body。而container此時是個div,所以它會是baseElement的child

const { baseElement } = rende(<p>this is NOT in Portal</p>);

console.log(baseElement.outerHTML); 

// <body>
//    <div></div>
//    <p>this is in Portal</p>
// </body>

但是需要注意的是有用到portal component時,因為portal component會直接被插到body下面,所以container的那個div內就會是空的,所以只能用baseElement

const { container } = render(
      <Portal>
        <p>this is in Portal</p>
      </Portal>,
);

console.log(container.outerHTML);

// <div></div>


const { baseElement } = render(
      <Portal>
        <p>this is in Portal</p>
      </Portal>,
);

console.log(baseElement.outerHTML);

// <body>
//    <div>
//       <p>this is NOT in Portal</p>
//    </div>
// </body>

也就是說container是包住要渲染的組件的那個div,而baseElement是body

const { container, baseElement } = render(
      <Portal>
        <p>this is in Portal</p>
      </Portal>,
    );

通常兩者都可以使用,但若測試的組件有用到 MUI 的 portal component 或者 React 的 createPortal(),因為 portal component 是被插在 body 裡,而不是 container 最外層的 div 則只能用 baseElement

Why are material UI components not rendered in jsdom when using Jest?
React Testing Library - API
【Day12】插槽 Portals
【Day21】導航元件 - Drawer


Mui theme因為沒有context而無法用

需要用到react-tesing-library的render,加上Mui的ThemeProvider自行包成另一個render function

// renderApp.ts

import { FunctionComponent, PropsWithChildren, ReactElement } from 'react';
import { RenderOptions, RenderResult, render } from '@testing-library/react';
import { ThemeProvider } from '@mui/material/styles';
import { useMyTheme } from './src/styles/theme';

const renderApp = (children: ReactElement, options?: RenderOptions): RenderResult => {
  const theme = useMyTheme();

  return render(
    <ThemeProvider theme={theme}>{children}</ThemeProvider>
  );
};

#react-testing-library #Jest #component test







Related Posts

Day 163

Day 163

用 Nest.js 開發 API 吧 (七) - Entity

用 Nest.js 開發 API 吧 (七) - Entity

JavaScript 進階 05:this

JavaScript 進階 05:this


Comments