คู่มือฉบับสมบูรณ์สำหรับการทดสอบ React Hooks
เผยแพร่แล้ว: 2022-03-11Hooks ถูกนำมาใช้ใน React 16.8 ในปลายปี 2018 เป็นฟังก์ชันที่เชื่อมต่อกับส่วนประกอบที่ใช้งานได้ และช่วยให้เราใช้คุณสมบัติสถานะและส่วนประกอบ เช่น componentDidUpdate
, componentDidMount
และอื่นๆ ก่อนหน้านี้ไม่สามารถทำได้
นอกจากนี้ hooks ยังช่วยให้เรานำส่วนประกอบกลับมาใช้ใหม่และระบุตรรกะในส่วนประกอบต่างๆ ได้อีกด้วย นี้เป็นเรื่องยากที่จะทำมาก่อน ดังนั้น hooks จึงเป็นตัวเปลี่ยนเกม
ในบทความนี้ เราจะมาศึกษาวิธีทดสอบ React Hooks เราจะเลือกเบ็ดที่ซับซ้อนเพียงพอและดำเนินการทดสอบ
เราหวังว่าคุณจะเป็นนักพัฒนา React ตัวยงที่คุ้นเคยกับ React Hooks อยู่แล้ว ในกรณีที่คุณต้องการปัดเศษความรู้ของคุณ คุณควรตรวจสอบบทช่วยสอนของเรา และนี่คือลิงค์ไปยังเอกสารอย่างเป็นทางการ
ตะขอที่เราจะใช้สำหรับการทดสอบ
สำหรับบทความนี้ เราจะใช้ hook ที่ฉันเขียนไว้ในบทความก่อนหน้าของฉัน นั่นคือ Stale-while-revalidate Data Fetching with React Hooks เบ็ดเรียกว่า useStaleRefresh
หากคุณยังไม่ได้อ่านบทความนี้ ไม่ต้องกังวล เพราะผมจะสรุปส่วนนั้นไว้ที่นี่
นี่คือเบ็ดที่เราจะทำการทดสอบ:
import { useState, useEffect } from "react"; const CACHE = {}; export default function useStaleRefresh(url, defaultValue = []) { const [data, setData] = useState(defaultValue); const [isLoading, setLoading] = useState(true); useEffect(() => { // cacheID is how a cache is identified against a unique request const cacheID = url; // look in cache and set response if present if (CACHE[cacheID] !== undefined) { setData(CACHE[cacheID]); setLoading(false); } else { // else make sure loading set to true setLoading(true); setData(defaultValue); } // fetch new data fetch(url) .then((res) => res.json()) .then((newData) => { CACHE[cacheID] = newData; setData(newData); setLoading(false); }); }, [url, defaultValue]); return [data, isLoading]; }
อย่างที่คุณเห็น useStaleRefresh
เป็น hook ที่ช่วยดึงข้อมูลจาก URL ในขณะที่ส่งคืนข้อมูลเวอร์ชันแคช หากมี ใช้ที่จัดเก็บในหน่วยความจำอย่างง่ายเพื่อเก็บแคช
นอกจากนี้ยังส่งกลับค่า isLoading
ที่เป็นจริงถ้ายังไม่มีข้อมูลหรือแคช ลูกค้าสามารถใช้เพื่อแสดงตัวบ่งชี้การโหลด ค่า isLoading
ถูกตั้งค่าเป็นเท็จเมื่อมีแคชหรือการตอบสนองใหม่
ณ จุดนี้ ฉันจะแนะนำให้คุณใช้เวลาอ่านส่วนเสริมด้านบนเพื่อทำความเข้าใจอย่างถ่องแท้ว่ามันคืออะไร
ในบทความนี้ เราจะมาดูกันว่าเราจะทดสอบ hook นี้ได้อย่างไร อันดับแรกโดยไม่ต้องใช้ไลบรารีทดสอบ (เฉพาะ React Test Utilities และ Jest) จากนั้นใช้ react-hooks-testing-library
แรงจูงใจเบื้องหลังการไม่ใช้ไลบรารีทดสอบ กล่าวคือ มีเพียงผู้ทดสอบ Jest
เท่านั้น คือการแสดงให้เห็นว่าการทดสอบฮุคทำงานอย่างไร ด้วยความรู้นั้น คุณจะสามารถดีบักปัญหาใด ๆ ที่อาจเกิดขึ้นเมื่อใช้ไลบรารีที่มีการทดสอบนามธรรม
การกำหนดกรณีทดสอบ
ก่อนที่เราจะเริ่มต้นการทดสอบเบ็ดนี้ เรามาวางแผนว่าเราต้องการทดสอบอะไรก่อน เนื่องจากเราทราบดีว่าตะขอควรทำอย่างไร นี่คือแผนแปดขั้นตอนของฉันสำหรับการทดสอบ:
- เมื่อติดตั้ง hook ด้วย URL
url1
isLoading
จะเป็นtrue
และข้อมูลจะเป็นdefaultValue
- หลังจากการร้องขอการดึงข้อมูลแบบอะซิงโครนัส hook จะถูกอัพเดตด้วย data
data1
และisLoading
เป็นfalse
- เมื่อ URL ถูกเปลี่ยนเป็น
url2
isLoading
จะกลายเป็น true อีกครั้งและข้อมูลจะเป็นdefaultValue
- หลังจากการร้องขอการดึงข้อมูลแบบอะซิงโครนัส hook จะถูกอัพเดตด้วย data ใหม่
data2
- จากนั้น เราเปลี่ยน URL กลับเป็น
url1
ข้อมูลdata1
จะได้รับทันทีเนื่องจากถูกแคชisLoading
เป็นเท็จ - หลังจากการร้องขอการดึงข้อมูลแบบอะซิงโครนัส เมื่อได้รับการตอบกลับใหม่ ข้อมูลจะถูกอัพเดตเป็น
data3
- จากนั้นเราเปลี่ยน URL กลับเป็น
url2
ข้อมูลdata2
จะได้รับทันทีเนื่องจากถูกแคชisLoading
เป็นเท็จ - หลังจากการร้องขอการดึงข้อมูลแบบอะซิงโครนัส เมื่อได้รับการตอบกลับใหม่ ข้อมูลจะถูกอัพเดตเป็น
data4
ขั้นตอนการทดสอบที่กล่าวถึงข้างต้นกำหนดวิถีการทำงานของเบ็ดไว้อย่างชัดเจน ดังนั้น หากเรามั่นใจว่าการทดสอบนี้ได้ผล เราก็ทำได้ดี
การทดสอบตะขอที่ไม่มีห้องสมุด
ในส่วนนี้ เราจะมาดูวิธีทดสอบ hooks โดยไม่ต้องใช้ไลบรารี่ สิ่งนี้จะช่วยให้เราเข้าใจในเชิงลึกเกี่ยวกับวิธีทดสอบ React Hooks
เพื่อเริ่มการทดสอบนี้ ก่อนอื่น เราต้องการจำลองการ fetch
เพื่อให้เราควบคุมสิ่งที่ API ส่งคืนได้ นี่คือการ fetch
ล้อเลียน
function fetchMock(url, suffix = "") { return new Promise((resolve) => setTimeout(() => { resolve({ json: () => Promise.resolve({ data: url + suffix, }), }); }, 200 + Math.random() * 300) ); }
การ fetch
ที่แก้ไขนี้จะถือว่าประเภทการตอบกลับเป็น JSON เสมอ และโดยค่าเริ่มต้น จะส่งคืน url
พารามิเตอร์เป็นค่า data
นอกจากนี้ยังเพิ่มการหน่วงเวลาแบบสุ่มระหว่าง 200ms ถึง 500ms ให้กับการตอบสนอง
หากเราต้องการเปลี่ยนการตอบสนอง เราเพียงแค่ตั้งค่า suffix
อาร์กิวเมนต์ที่สองเป็นค่าสตริงที่ไม่ว่าง
ณ จุดนี้คุณอาจถามว่าทำไมล่าช้า? ทำไมเราไม่ตอบกลับทันที เนื่องจากเราต้องการจำลองโลกแห่งความเป็นจริงให้มากที่สุด เราไม่สามารถทดสอบเบ็ดได้อย่างถูกต้องหากเราส่งคืนทันที แน่นอนว่า เราสามารถลดดีเลย์ลงเหลือ 50-100ms เพื่อการทดสอบที่เร็วขึ้น แต่ไม่ต้องกังวลไปในบทความนี้
ด้วยการจำลองการดึงข้อมูลของเรา เราสามารถตั้งค่าให้เป็นฟังก์ชัน fetch
ได้ เราใช้ beforeAll
และ afterAll
ในการดำเนินการดังกล่าว เนื่องจากฟังก์ชันนี้ไม่มีสถานะ ดังนั้นเราจึงไม่จำเป็นต้องรีเซ็ตหลังจากการทดสอบแต่ละครั้ง
// runs before any tests start running beforeAll(() => { jest.spyOn(global, "fetch").mockImplementation(fetchMock); }); // runs after all tests have finished afterAll(() => { global.fetch.mockClear(); });
จากนั้น เราต้องติดตะขอในส่วนประกอบ ทำไม? เพราะตะขอเป็นเพียงหน้าที่ของมันเอง เฉพาะเมื่อใช้ในส่วนประกอบเท่านั้นที่สามารถตอบสนองต่อ useState
, useEffect
ฯลฯ
ดังนั้น เราจำเป็นต้องสร้าง TestComponent
ที่ช่วยให้เราติดตั้ง hook ของเรา
// defaultValue is a global variable to avoid changing the object pointer on re-render // we can also deep compare `defaultValue` inside the hook's useEffect const defaultValue = { data: "" }; function TestComponent({ url }) { const [data, isLoading] = useStaleRefresh(url, defaultValue); if (isLoading) { return <div>loading</div>; } return <div>{data.data}</div>; }
นี่เป็นองค์ประกอบง่ายๆ ที่แสดงข้อมูลหรือแสดงข้อความ "กำลังโหลด" หากข้อมูลกำลังโหลด (กำลังดึงข้อมูล)
เมื่อเราได้ส่วนประกอบทดสอบแล้ว เราต้องติดตั้งบน DOM เราใช้ beforeEach
และ afterEach
เพื่อเมานต์และยกเลิกการต่อเชื่อมส่วนประกอบของเราสำหรับการทดสอบแต่ละครั้ง เนื่องจากเราต้องการเริ่มต้นด้วย DOM ใหม่ก่อนการทดสอบแต่ละครั้ง
let container = null; beforeEach(() => { // set up a DOM element as a render target container = document.createElement("div"); document.body.appendChild(container); }); afterEach(() => { // cleanup on exiting unmountComponentAtNode(container); container.remove(); container = null; });
โปรดสังเกตว่า container
ต้องเป็นตัวแปรส่วนกลาง เนื่องจากเราต้องการเข้าถึงคอนเทนเนอร์เพื่อยืนยันการทดสอบ
ด้วยชุดดังกล่าว เรามาทำการทดสอบครั้งแรกของเราที่แสดงผล URL url1
และเนื่องจากการดึง URL จะใช้เวลาพอสมควร (ดู fetchMock
) จึงควรแสดงข้อความ "กำลังโหลด" ในขั้นต้น
it("useStaleRefresh hook runs correctly", () => { act(() => { render(<TestComponent url="url1" />, container); }); expect(container.textContent).toBe("loading"); })
รันการทดสอบโดยใช้ yarn test
และทำงานได้ตามที่คาดไว้ นี่คือรหัสที่สมบูรณ์บน GitHub
ตอนนี้ มาทดสอบกันเมื่อข้อความ loading
นี้เปลี่ยนเป็นข้อมูลตอบกลับที่ดึงมา url1
เราจะทำอย่างนั้นได้อย่างไร? หากคุณดูที่ fetchMock
คุณจะเห็นว่าเรารอ 200-500 มิลลิวินาที จะเกิดอะไรขึ้นถ้าเรา sleep
การทดสอบที่รอ 500 มิลลิวินาที จะครอบคลุมเวลารอที่เป็นไปได้ทั้งหมด ลองทำกันดู
function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } it("useStaleRefresh hook runs correctly", async () => { act(() => { render(<TestComponent url="url1" />, container); }); expect(container.textContent).toBe("loading"); await sleep(500); expect(container.textContent).toBe("url1"); });
การทดสอบผ่าน แต่เราเห็นข้อผิดพลาดเช่นกัน (รหัส)
PASS src/useStaleRefresh.test.js ✓ useStaleRefresh hook runs correctly (519ms) console.error node_modules/react-dom/cjs/react-dom.development.js:88 Warning: An update to TestComponent inside a test was not wrapped in act(...).
นี่เป็นเพราะการอัพเดตสถานะใน useStaleRefresh
hook เกิดขึ้นนอก act() เพื่อให้แน่ใจว่าการอัปเดต DOM ได้รับการประมวลผลอย่างทันท่วงที React แนะนำให้คุณใช้ act()
ทุกครั้งที่มีการเรนเดอร์ซ้ำหรืออาจมีการอัปเดต UI ดังนั้น เราต้องปิดฉากการนอนของเราด้วย act
เนื่องจากเป็นเวลาที่สถานะอัปเดตเกิดขึ้น หลังจากทำเช่นนั้น ข้อผิดพลาดจะหายไป
import { act } from "react-dom/test-utils"; // ... await act(() => sleep(500));
ตอนนี้ เรียกใช้อีกครั้ง (รหัสบน GitHub) ตามที่คาดไว้ มันจะผ่านไปโดยไม่มีข้อผิดพลาด
มาทดสอบสถานการณ์ต่อไปที่เราเปลี่ยน URL เป็น url2
ก่อน จากนั้นตรวจสอบหน้าจอการ loading
จากนั้นรอการตอบกลับในการดึงข้อมูล และสุดท้ายตรวจสอบข้อความ url2
เนื่องจากตอนนี้เรารู้วิธีรอการเปลี่ยนแปลงแบบ async อย่างถูกต้องแล้ว สิ่งนี้น่าจะง่าย

act(() => { render(<TestComponent url="url2" />, container); }); expect(container.textContent).toContain("loading"); await act(() => sleep(500)); expect(container.textContent).toBe("url2");
เรียกใช้การทดสอบนี้และจะผ่านเช่นกัน ตอนนี้ เรายังสามารถทดสอบกรณีที่ข้อมูลการตอบสนองเปลี่ยนแปลงและแคชเข้ามาเล่น
คุณจะสังเกตเห็นว่าเรามี suffix
อาร์กิวเมนต์เพิ่มเติมในฟังก์ชัน fetchMock ของเรา ใช้สำหรับเปลี่ยนข้อมูลตอบกลับ ดังนั้นเราจึงอัปเดต fetch mock เพื่อใช้ suffix
global.fetch.mockImplementation((url) => fetchMock(url, "__"));
ตอนนี้ เราสามารถทดสอบกรณีที่ URL ถูกตั้งค่าเป็น url1
อีกครั้ง มันโหลด url1
ก่อนแล้วจึง url1__
เราสามารถทำเช่นเดียวกันสำหรับ url2
และไม่ควรมีอะไรน่าประหลาดใจ
it("useStaleRefresh hook runs correctly", async () => { // ... // new response global.fetch.mockImplementation((url) => fetchMock(url, "__")); // set url to url1 again act(() => { render(<TestComponent url="url1" />, container); }); expect(container.textContent).toBe("url1"); await act(() => sleep(500)); expect(container.textContent).toBe("url1__"); // set url to url2 again act(() => { render(<TestComponent url="url2" />, container); }); expect(container.textContent).toBe("url2"); await act(() => sleep(500)); expect(container.textContent).toBe("url2__"); });
การทดสอบทั้งหมดนี้ทำให้เรามั่นใจว่า hook ทำงานได้ตามที่คาดไว้ (รหัส) เย่! ตอนนี้ มาดูการเพิ่มประสิทธิภาพอย่างรวดเร็วโดยใช้วิธีตัวช่วย
เพิ่มประสิทธิภาพการทดสอบโดยใช้ Helper Methods
จนถึงตอนนี้ เราได้เห็นวิธีทดสอบเบ็ดของเราแล้ว วิธีการนี้ไม่สมบูรณ์แบบแต่ได้ผล และเราสามารถทำได้ดีกว่านี้ไหม
ใช่. ขอให้สังเกตว่าเรากำลังรอ 500ms คงที่สำหรับการดึงข้อมูลแต่ละครั้งจะเสร็จสมบูรณ์ แต่คำขอแต่ละรายการจะใช้เวลาตั้งแต่ 200 ถึง 500 มิลลิวินาที เห็นได้ชัดว่าเรากำลังเสียเวลาอยู่ที่นี่ เราสามารถจัดการได้ดีขึ้นโดยเพียงแค่รอเวลาที่แต่ละคำขอใช้
เราจะทำอย่างนั้นได้อย่างไร? เทคนิคง่ายๆ คือการดำเนินการยืนยันจนกว่าจะผ่านหรือหมดเวลา มาสร้างฟังก์ชัน waitFor
ที่ทำอย่างนั้นกันเถอะ
async function waitFor(cb, timeout = 500) { const step = 10; let timeSpent = 0; let timedOut = false; while (true) { try { await sleep(step); timeSpent += step; cb(); break; } catch {} if (timeSpent >= timeout) { timedOut = true; break; } } if (timedOut) { throw new Error("timeout"); } }
ฟังก์ชันนี้เรียกใช้การเรียกกลับ (cb) ภายในบล็อก try...catch
ทุก ๆ 10 มิลลิวินาที และหาก timeout
มันจะส่งข้อผิดพลาด สิ่งนี้ทำให้เราสามารถดำเนินการยืนยันได้จนกว่าจะผ่านอย่างปลอดภัย (กล่าวคือ ไม่มีลูปอนันต์)
เราสามารถใช้ในการทดสอบของเราได้ดังนี้: แทนที่จะนอน 500ms แล้วยืนยัน เราใช้ฟังก์ชัน waitFor
ของเรา
// INSTEAD OF await act(() => sleep(500)); expect(container.textContent).toBe("url1"); // WE DO await act(() => waitFor(() => { expect(container.textContent).toBe("url1"); }) );
ทำในการยืนยันดังกล่าวทั้งหมด และเราจะเห็นความแตกต่างอย่างมากในความเร็วของการทดสอบของเรา (โค้ด)
ทั้งหมดนี้ดีมาก แต่บางทีเราอาจไม่ต้องการทดสอบ hook ผ่าน UI บางทีเราต้องการทดสอบ hook โดยใช้ค่าที่ส่งคืน เราจะทำอย่างนั้นได้อย่างไร?
ไม่ใช่เรื่องยากเพราะเราสามารถเข้าถึงค่าส่งคืนของ hook ได้แล้ว พวกมันอยู่ภายในองค์ประกอบเท่านั้น หากเราสามารถนำตัวแปรเหล่านั้นออกไปสู่โกลบอล สโคป มันก็จะได้ผล มาทำกัน
เนื่องจากเราจะทดสอบ hook ของเราผ่านค่าที่ส่งคืนและไม่ได้แสดงผล DOM เราจึงสามารถลบการเรนเดอร์ HTML ออกจากส่วนประกอบของเราและทำให้แสดงผลเป็น null
ได้ เราควรลบการทำลายล้างในการกลับมาของ hook เพื่อให้เป็นแบบทั่วไปมากขึ้น ดังนั้นเราจึงมีองค์ประกอบการทดสอบที่อัปเดตนี้
// global variable let result; function TestComponent({ url }) { result = useStaleRefresh(url, defaultValue); return null; }
ตอนนี้ค่าส่งคืนของ hook ถูกเก็บไว้ใน result
ซึ่งเป็นตัวแปรส่วนกลาง เราสามารถสอบถามเพื่อยืนยันได้
// INSTEAD OF expect(container.textContent).toContain("loading"); // WE DO expect(result[1]).toBe(true); // INSTEAD OF expect(container.textContent).toBe("url1"); // WE DO expect(result[0].data).toBe("url1");
หลังจากที่เราเปลี่ยนมันทุกที่ เราจะเห็นว่าการทดสอบของเราผ่าน (รหัส)
ณ จุดนี้ เราได้รับส่วนสำคัญของการทดสอบ React Hooks ยังมีการปรับปรุงบางอย่างที่เราสามารถทำได้ เช่น:
- การย้ายตัวแปร
result
ไปยังขอบเขตท้องถิ่น - ขจัดความจำเป็นในการสร้างส่วนประกอบสำหรับทุกๆ hook ที่เราต้องการทดสอบ
เราสามารถทำได้โดยการสร้างฟังก์ชันโรงงานที่มีส่วนประกอบทดสอบอยู่ภายใน นอกจากนี้ยังควรแสดง hook ในองค์ประกอบการทดสอบและให้สิทธิ์เราเข้าถึงตัวแปร result
มาดูกันว่าเราจะทำได้อย่างไร
ขั้นแรก เราย้าย TestComponent
และ result
ภายในฟังก์ชัน นอกจากนี้เรายังต้องส่งข้อโต้แย้งของ Hook และ Hook เป็นอาร์กิวเมนต์ของฟังก์ชันเพื่อให้สามารถใช้ในองค์ประกอบการทดสอบของเราได้ ใช้สิ่งนั้นนี่คือสิ่งที่เรามี เรากำลังเรียกใช้ฟังก์ชันนี้ renderHook
function renderHook(hook, args) { let result = {}; function TestComponent({ hookArgs }) { result.current = hook(...hookArgs); return null; } act(() => { render(<TestComponent hookArgs={args} />, container); }); return result; }
เหตุผลที่เราได้ result
เป็นวัตถุที่เก็บข้อมูลใน result.current
เป็นเพราะเราต้องการให้ค่าที่ส่งคืนได้รับการอัปเดตเมื่อทำการทดสอบ ค่าส่งคืนของ hook ของเราคืออาร์เรย์ ดังนั้นมันจะถูกคัดลอกโดยค่าถ้าเราส่งคืนโดยตรง โดยการจัดเก็บไว้ในอ็อบเจ็กต์ เราจะคืนค่าการอ้างอิงไปยังอ็อบเจ็กต์นั้น เพื่อให้สามารถอัปเดตค่าที่ส่งคืนได้โดยการอัพเดต result.current
ตอนนี้เราจะอัปเดต hook อย่างไร เนื่องจากเราใช้การปิดอยู่แล้ว ให้ใส่ฟังก์ชัน rerender
ที่สามารถทำได้
ฟังก์ชัน renderHook
สุดท้ายมีลักษณะดังนี้:
function renderHook(hook, args) { let result = {}; function TestComponent({ hookArgs }) { result.current = hook(...hookArgs); return null; } function rerender(args) { act(() => { render(<TestComponent hookArgs={args} />, container); }); } rerender(args); return { result, rerender }; }
ตอนนี้เราสามารถใช้มันในการทดสอบของเราได้ แทนที่จะใช้ act
และ render
เราทำสิ่งต่อไปนี้:
const { rerender, result } = renderHook(useStaleRefresh, [ "url1", defaultValue, ]);
จากนั้นเราสามารถยืนยันโดยใช้ result.current
และอัปเดต hook โดยใช้การ rerender
นเดอร์ นี่เป็นตัวอย่างง่ายๆ:
rerender(["url2", defaultValue]); expect(result.current[1]).toBe(true); // check isLoading is true
เมื่อคุณเปลี่ยนในทุกตำแหน่งแล้ว คุณจะเห็นว่ามันทำงานโดยไม่มีปัญหาใดๆ (รหัส)
ฉลาดหลักแหลม! ตอนนี้ เรามีสิ่งที่เป็นนามธรรมที่ชัดเจนยิ่งขึ้นสำหรับการทดสอบ hooks เรายังสามารถทำได้ดีกว่านี้ เช่น ต้องส่งค่า defaultValue
ทุกครั้งเพื่อ rerender
แม้ว่าจะไม่เปลี่ยนแปลง เราสามารถแก้ไขได้
แต่อย่าตีกันมากเกินไป เพราะเรามีห้องสมุดที่ช่วยปรับปรุงประสบการณ์นี้ได้อย่างมาก
ป้อน react-hooks-testing-library
การทดสอบโดยใช้ React-hooks-testing-library
React-hooks-testing-library ทำทุกอย่างที่เราเคยพูดถึงมาก่อนแล้วบ้าง ตัวอย่างเช่น จะจัดการการติดตั้งและการยกเลิกการต่อเชื่อมคอนเทนเนอร์ ดังนั้นคุณจึงไม่ต้องดำเนินการดังกล่าวในไฟล์ทดสอบของคุณ วิธีนี้ช่วยให้เราจดจ่อกับการทดสอบเบ็ดของเราได้โดยไม่เสียสมาธิ
มันมาพร้อมกับฟังก์ชั่น renderHook
ที่ส่งคืนการเรน rerender
และ result
นอกจากนี้ยังส่งคืน wait
ซึ่งคล้ายกับ waitFor
ดังนั้นคุณจึงไม่ต้องดำเนินการด้วยตนเอง
นี่คือวิธีที่เราสร้าง hook ใน React-hooks-testing-library สังเกตว่า hook ถูกส่งผ่านในรูปแบบของการโทรกลับ การเรียกกลับนี้จะดำเนินการทุกครั้งที่องค์ประกอบการทดสอบแสดงผลซ้ำ
const { result, wait, rerender } = renderHook( ({ url }) => useStaleRefresh(url, defaultValue), { initialProps: { url: "url1", }, } );
จากนั้น เราสามารถทดสอบว่าการเรนเดอร์แรกส่งผลให้ isLoading
เป็นจริงและคืนค่าเป็น defaultValue
โดยทำเช่นนี้ คล้ายกับสิ่งที่เราดำเนินการข้างต้น
expect(result.current[0]).toEqual(defaultValue); expect(result.current[1]).toBe(true);
ในการทดสอบการอัปเดตแบบอะซิงโครนัส เราสามารถใช้วิธี wait
ที่ renderHook
ส่งคืน มันมาพร้อมกับ act()
ดังนั้นเราจึงไม่จำเป็นต้องล้อม act()
รอบๆ
await wait(() => { expect(result.current[0].data).toEqual("url1"); }); expect(result.current[1]).toBe(false);
จากนั้น เราสามารถใช้การ rerender
เดอร์เพื่ออัปเดตด้วยอุปกรณ์ประกอบฉากใหม่ สังเกตว่าเราไม่จำเป็นต้องส่งค่า defaultValue
ที่นี่
rerender({ url: "url2" });
สุดท้าย การทดสอบที่เหลือจะดำเนินการในลักษณะเดียวกัน (รหัส)
ห่อ
เป้าหมายของฉันคือแสดงวิธีทดสอบ React Hooks โดยยกตัวอย่าง async hook ฉันหวังว่าสิ่งนี้จะช่วยให้คุณจัดการกับการทดสอบเบ็ดทุกชนิดอย่างมั่นใจ เนื่องจากแนวทางเดียวกันนี้ควรนำไปใช้กับพวกเขาส่วนใหญ่
ฉันอยากจะแนะนำให้คุณใช้ React-hooks-testing-library เนื่องจากมันเสร็จสมบูรณ์แล้ว และฉันยังไม่เจอปัญหาสำคัญกับมันจนถึงตอนนี้ ในกรณีที่คุณพบปัญหา ตอนนี้คุณรู้วิธีแก้ไขปัญหาโดยใช้ตะขอทดสอบที่อธิบายในบทความนี้แล้ว