iT邦幫忙

2022 iThome 鐵人賽

DAY 21
0
自我挑戰組

<< 測試魔法 >> 這能動嗎?不然就測測看好了!系列 第 21

情境練習:透過 Mock 模擬 Module

  • 分享至 

  • xImage
  •  

今天要來改寫昨天的範例程式碼,嘗試透過 Context 撰寫 todoStore.js 放置 todoList 所需的 Data 資料,並透過 mock 模擬整個 todoStore 進行測試,防止報錯及降低元件間的相依性。

步驟一、首先來封裝一個 store

於 store 內傳送 fetchData 資料:

import React, { useContext } from 'react';
import axios from 'axios';

export const TodoDataContext = React.createContext();

export const useTodoStore = () => useContext(TodoDataContext);

export const TodoContextProvider = ({ children }) => {
  const fetchData = async () => {
    const response = await axios.get(
      `https://jsonplaceholder.typicode.com/todos`
    );
    return response.data;
  };

  return (
    <TodoDataContext.Provider
      value={{
        fetchData,
      }}
    >
      {children}
    </TodoDataContext.Provider>
  );
};

步驟二、在 TodoList 的父層級包覆 TodoContextProvider 使 TodoList 能順利取得資料

import React from 'react';
import './App.css';
import TodoList from './components/compos/TodoList';
import axios from 'axios';

import { TodoContextProvider } from './stores/todoStore';

const App = () => {
  const fetchData = async () => {
    const response = await axios.get(
      `https://jsonplaceholder.typicode.com/todos`
    );
    return response.data;
  };

  return (
    <>
      <TodoContextProvider>
        <TodoList />
      </TodoContextProvider>
    </>
  );
};

export default App;

步驟三、在 TodoList 內取用 useTodoStore 內的 fetchData 函式,並於 init 時呼叫取得值後傳入

import React, { useEffect, useState } from 'react';
import { useTodoStore } from '@/stores/todoStore';

const TodoList = () => {
  const { fetchData } = useTodoStore();
  const [todoData, setTodoData] = useState(null);

  const init = async () => {
    const data = await fetchData();
    setTodoData(data);
  };

  useEffect(() => {
    init();
  }, []);

  return (
    <>
      <ul>
        {todoData &&
          todoData.map((item) => <li key={item.id}>{item.title}</li>)}
      </ul>
    </>
  );
};
export default TodoList;

Mock module

首先來看一下 Jest 文件內 Mock 元件的方法:

  • '../moduleName' 需要帶入要模擬 module 路徑
  • 第二個參數為函式,內層回傳模擬的函式或值
jest.mock('../moduleName', () => {
  return jest.fn(() => 42);
});

// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';

接下來來看要怎麼模擬 store 吧?

首先第一個參數路徑的部分要帶入 store 的位置,內層先回傳透過具名匯出的 useTodoStore ,store 內層記得將我們要使用的 fetchData 透過 jest.fn() 進行模擬,並使用 mockReturnValue 回傳設定好的模擬值:

import { render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import TodoList from './TodoList';

jest.mock('@/stores/todoStore', () => ({
  useTodoStore: () => ({
    fetchData: jest.fn().mockReturnValue([{ id: 'test', title: 'title' }]),
  }),
}));

store 模擬完成後,可以開始進行測試了!

測試的部分透過 render 渲染出 TodoList 元件後,透過 waitFor 處理非同步行為,最後斷言設定的 title 模擬值能正確呈現!

test('Test get value from Store', async () => {
  render(<TodoList />);

  await waitFor(() => {
    expect(screen.getByText('title')).toBeInTheDocument();
  });
});

運行測試後,可以看見測試的結果:
https://ithelp.ithome.com.tw/upload/images/20221006/20139066HDtyFWLZtu.png
如果嘗試透過 screen.debug() 也能看到印出後的 DOM 有成功顯示 title !
https://ithelp.ithome.com.tw/upload/images/20221006/20139066jOirBA4AR0.png
今天的測試小練習成功,明天也繼續加油吧!


參考文章

https://jestjs.io/docs/jest-object#jestmockmodulename-factory-options


上一篇
情境練習:使用 Context 報錯情境
下一篇
Mock Service Worker 學習(一)
系列文
<< 測試魔法 >> 這能動嗎?不然就測測看好了!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言