Vitest · Mocks และ Spies
`.mockResolvedValue()`
กำหนดค่าที่ mock function จะ resolve กลับแบบ asynchronous
mockResolvedValue()
mockResolvedValue() กำหนดค่าที่ mock function จะ resolve กลับมาในรูป Promise เหมาะกับ repository หรือ API client แบบ async ทำให้ test ควบคุมข้อมูลที่ service ได้รับโดยไม่เรียก network หรือฐานข้อมูลจริง
ค่าที่กำหนดจะถูกห่อเป็น Promise ที่ resolve
กฎสำคัญ
ตรวจ interaction ที่เป็น contract
mock matcher ควรพิสูจน์การสื่อสารที่จำเป็นต่อ behavior ไม่ใช่ตรวจทุกการเรียกภายใน
อ่าน call history เป็นหลักฐาน
จำนวนครั้งและ arguments ช่วยบอกว่า boundary ได้รับข้อมูลตามที่คาดหรือไม่
แกะ mock จากการเตรียมไปถึง assertion
ตัวอย่าง mock ควรอ่านเป็นลำดับ: สร้างตัวแทน, กำหนดหรือปล่อยให้มันบันทึก behavior, เรียก subject และตรวจหลักฐานที่ผ่าน boundary ตารางนี้ช่วยแยกขั้นที่มักถูกเขียนติดกันจนผู้เริ่มต้นมองไม่ออก
| ขั้น | สิ่งที่เกิดขึ้น |
|---|---|
| สร้าง boundary | สร้าง repository.findById เป็น mock function |
| กำหนด async result | mockResolvedValue ทำให้การเรียกคืน Promise ที่ resolve เป็น Task |
| รอและตรวจ | ใช้ await กับ resolves เพื่อยืนยันค่าที่ repository ส่งกลับ |
อย่าเริ่มจากการจำชื่อ matcher อย่างเดียว ให้ถามก่อนว่า mockResolvedValue() กำลังพิสูจน์การสื่อสารข้อใด และการเรียกนั้นเป็น contract จริงหรือเป็นเพียงรายละเอียดภายใน
เลือก assertion ให้พอดี
| สถานการณ์ | แนวทาง |
|---|---|
| dependency sync | mockReturnValue |
| dependency async success | mockResolvedValue |
| ต้องการทดสอบ error ของ async dependency | ใช้ rejects กับ mock ที่คืน rejected Promise ใน scope ขั้นถัดไป |
Mock ไม่ใช่เป้าหมายของ test
เริ่มจาก behavior ของ Task service แล้วค่อยตรวจ interaction ที่จำเป็นต่อ behavior นั้น ถ้าเปลี่ยน implementation แล้วยังรักษา contract ได้ test ไม่ควร fail เพียงเพราะรูปแบบภายในเปลี่ยน
แบบฝึก: ตรวจ interaction
จำลอง repository ที่คืน Task สำเร็จ
งานที่ต้องทำ
- สร้าง repository.findById ด้วย vi.fn()
- กำหนด mockResolvedValue เป็น Task id 2
- ใช้ await และ resolves ตรวจข้อมูล
ลองทำด้วยตัวเองก่อน แล้วค่อยเปิดเฉลยเพื่อเทียบแนวคิดและรายละเอียด