今天來實作模擬 Call API 測試吧!
測試情境:我要 Call API 拿到回傳 User 資料,並顯示在畫面上。
前情提要:昨天已經完成 MSW 模擬 API 回傳設定:
// mocks/handler
export const handlers = [
rest.get("https://jsonplaceholder.typicode.com/users", (req, res, ctx) => {
return(
res(
ctx.status(200),
ctx.json([
{
"id": 1,
"name": "Leanne Graham",
"username": "Bret",
"email": "Sincere@april.biz",
"address": {
"street": "Kulas Light",
"suite": "Apt. 556",
"city": "Gwenborough",
"zipcode": "92998-3874",
"geo": {
"lat": "-37.3159",
"lng": "81.1496"
}
},
"phone": "1-770-736-8031 x56442",
"website": "hildegard.org",
"company": {
"name": "Romaguera-Crona",
"catchPhrase": "Multi-layered client-server neural-net",
"bs": "harness real-time e-markets"
}
},
])
)
)
})
];
撰寫元件 Users
,畫面上顯示 username,資料來源為 API 回傳資料。
import { useEffect, useState } from "react"
import UserType from "./user.type";
export default function Users(){
const [users, setUsers] = useState<UserType[]>([]);
const [error, setError] = useState<string|null>(null);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then(res => res.json())
.then(data => {
setUsers(data);
setError(null);
})
.catch(err => {
setUsers([]);
setError("No Data");
})
}, [])
return(
<>
<h1>User List</h1>
{error && <h3>{error}</h3>}
{users.length > 0
&&
<ul>
{ users.map(item => (
<li key={item.id}>{item.username}</li>
))}
</ul>
}
</>
)
}
撰寫測試:取得 listitem
內容長度是否符合 MSW 模擬回傳長度(目前有一筆資料)。
describe("User", () => {
test("Render mutiple listItemes after calling API.", async () => {
render(<Users />);
const userItemElements = await screen.findAllByRole("listitem");
expect(userItemElements).toHaveLength(1);
})
})
測試結果:
FAIL src/components/users/user.test.tsx
● User › Render mutiple listItemes after calling API.
expect(received).toHaveLength(expected)
Expected length: 1
Received length: 10顯示錯誤原因:因為執行測試沒有使用 MSW,而是實際 Call API,預設會回傳 10 users 資料。
新增程式碼:加入MSW 官網:setupTests才能實際將 MSW 加入測試模擬。
// src/setupTests.ts
import { server } from './mocks/server.js'
// Establish API mocking before all tests.
beforeAll(() => server.listen())
// Reset any request handlers that we may add during the tests,
// so they don't affect other tests.
afterEach(() => server.resetHandlers())
// Clean up after the tests are finished.
afterAll(() => server.close())
** 補充說明:**beforeAll
afterEach
afterAll
是由 Jest 提供的方法。
三個函式都會有兩個參數:執行函式、函式等待時間(單位:毫秒,預設5秒,選填)
測試結果:
PASS src/components/users/user.test.tsx
剛剛測試API回傳成功是否符合預期時,我們多加了 setupTests.ts
讓每一次測試都會經過 MSW。
但是我們可以想想:
如果我現在要測試 API回傳失敗是否符合預期時,我直接 handler.ts
內的 cts.status(500)
,合適嗎?
handler.ts
即可呢?setupTests.ts
)來試試第二點與第三點怎麼實作吧!
撰寫測試:
import { render, screen } from "@testing-library/react";
import Users from "./users";
import { rest } from "msw";
import { server } from "../../mocks/server";
describe("User", () => {
test("Render Error Message after calling API.", async () => {
server.use(
// 建立單獨模擬 call API fail 的 handler
rest.get("https://jsonplaceholder.typicode.com/users", (_, res, ctx) => (
res(ctx.status(500))
))
)
render(<Users />);
const errorMsgEl = await screen.findByRole("heading", {
level: 3
});
expect(errorMsgEl).toHaveTextContent("No Data");
})
})
補充說明:
setupServer
設定。測試結果:
PASS src/components/users/user.test.tsx
故意寫錯測試錯誤訊息,來驗證一下測試有沒有成功:
測試結果:
FAIL src/components/users/user.test.tsx
● User › Render Error Message after calling API.expect(element).toHaveTextContent() Expected element to have text content: No Data! Received: No Data