Vitest · Mocks และ Spies
`.toHaveBeenCalledTimes()`
ตรวจจำนวนครั้งที่ mock หรือ spy ถูกเรียก
toHaveBeenCalledTimes()
toHaveBeenCalledTimes(n) ตรวจจำนวนครั้งที่ mock หรือ spy ถูกเรียก เหมาะเมื่อจำนวนเป็นส่วนหนึ่งของ behavior เช่น retry หนึ่งครั้ง หรือส่ง notification หนึ่งครั้งต่อการกระทำ
กฎสำคัญ
ตรวจ interaction ที่เป็น contract
mock matcher ควรพิสูจน์การสื่อสารที่จำเป็นต่อ behavior ไม่ใช่ตรวจทุกการเรียกภายใน
อ่าน call history เป็นหลักฐาน
จำนวนครั้งและ arguments ช่วยบอกว่า boundary ได้รับข้อมูลตามที่คาดหรือไม่
แกะ mock จากการเตรียมไปถึง assertion
ตัวอย่าง mock ควรอ่านเป็นลำดับ: สร้างตัวแทน, กำหนดหรือปล่อยให้มันบันทึก behavior, เรียก subject และตรวจหลักฐานที่ผ่าน boundary ตารางนี้ช่วยแยกขั้นที่มักถูกเขียนติดกันจนผู้เริ่มต้นมองไม่ออก
| ขั้น | สิ่งที่เกิดขึ้น |
|---|---|
| กำหนด contract | หนึ่งการกระทำควรส่ง notification หนึ่งครั้ง |
| ทำ action | เรียก function หรือ code path ที่ต้องการตรวจ |
| ตรวจ count | toHaveBeenCalledTimes(1) ยืนยันจำนวนที่เป็นส่วนหนึ่งของ behavior |
อย่าเริ่มจากการจำชื่อ matcher อย่างเดียว ให้ถามก่อนว่า toHaveBeenCalledTimes() กำลังพิสูจน์การสื่อสารข้อใด และการเรียกนั้นเป็น contract จริงหรือเป็นเพียงรายละเอียดภายใน
เลือก assertion ให้พอดี
| สถานการณ์ | แนวทาง |
|---|---|
| ไม่สนใจว่าจะเรียกกี่ครั้ง ขอให้มี interaction | toHaveBeenCalled() |
| ต้องเรียกครั้งเดียวตาม contract | toHaveBeenCalledTimes(1) |
| จำนวนเป็น implementation detail ที่เปลี่ยนได้ | อย่า assert count โดยไม่มีเหตุผล |
Mock ไม่ใช่เป้าหมายของ test
เริ่มจาก behavior ของ Task service แล้วค่อยตรวจ interaction ที่จำเป็นต่อ behavior นั้น ถ้าเปลี่ยน implementation แล้วยังรักษา contract ได้ test ไม่ควร fail เพียงเพราะรูปแบบภายในเปลี่ยน
แบบฝึก: ตรวจ interaction
ตรวจว่า completeAndNotify แจ้ง event เพียงหนึ่งครั้ง
งานที่ต้องทำ
- เรียก function หนึ่งครั้ง
- ตรวจจำนวน call เป็น 1
- อย่าเรียก mock ซ้ำใน Arrange โดยไม่ตั้งใจ
ลองทำด้วยตัวเองก่อน แล้วค่อยเปิดเฉลยเพื่อเทียบแนวคิดและรายละเอียด