อะไรคือความแตกต่างระหว่างจำลองและต้นขั้ว?


963

ฉันได้อ่านบทความต่าง ๆ เกี่ยวกับการเยาะเย้ยและการขัดถูในการทดสอบรวมถึงMocks Arsenal ของมาร์ตินฟาวเลอร์ไม่ใช่ Stubแต่ก็ยังไม่เข้าใจความแตกต่าง



75
@OP เพราะไม่มีความแตกต่าง บทความนี้มากพอ ๆ กับความรักของชุมชนคือ - ด้วยความเคารพอย่างสูง - ทำให้ทุกอย่างเกิดความสับสนโดยไม่จำเป็นโดยเพิ่มความหมายเพิ่มเติมให้กับคำที่ง่ายต่อการเข้าใจเป็นอย่างอื่น การเยาะเย้ยเป็นเพียงการเยาะเย้ยสิ่งที่ใช้ตรรกะทางธุรกิจปลอมแทนของจริง การตรวจสอบพฤติกรรมในที่สุดเป็นตัวเลือกของคุณ แต่ก็ยังเป็นเรื่องเยาะเย้ย หรืออะไรก็ตามที่คุณต้องการจะโทรหา อย่าแยกเส้นผม ทำให้ง่ายขึ้นเพื่อให้ผู้คนสามารถเข้าใจแนวคิดของคุณได้อย่างง่ายดาย - บทความด้านบนใดที่ล้มเหลว
59

10
"การจำแนกระหว่าง mocks, fakes และ stubs นั้นไม่สอดคล้องกันอย่างมากในวรรณคดี" ด้วยการอ้างอิงมากมาย ยังเป็นหนึ่งในคำพูดวิกิพีเดียที่ฉันโปรดปราน - หากมีสิ่งนั้นอยู่ :) en.wikipedia.org/wiki/Mock_object
JD

11
บทความของ Martin Fowler นั้นยากที่จะเข้าใจสำหรับผู้เริ่มต้น
lmiguelvargasf

1
วิธีที่ฉันเข้าใจก็คือต้นขั้วจะเป็นเพียงวัตถุที่ทิ้งสำหรับการทดสอบของคุณเช่นชุดของข้อมูลจำลอง Mock จะเป็นเวอร์ชันที่ถูกแทนที่อย่างชาญฉลาดของบางสิ่งที่ซับซ้อนมากขึ้นเช่นเลเยอร์บริการที่มีวิธีการต่าง ๆ ซึ่งคุณอาจเปลี่ยนพฤติกรรมของการทดสอบของคุณ มีการใช้สองสิ่งร่วมกันเช่นคุณสามารถส่งวัตถุที่ถูกขัดจังหวะไปยังเลเยอร์ที่เยาะเย้ยของคุณ
JsonStatham

คำตอบ:


746

ต้นขั้ว

ฉันเชื่อว่าความแตกต่างที่ยิ่งใหญ่ที่สุดคือต้นขั้วที่คุณเขียนไปแล้วพร้อมกับพฤติกรรมที่กำหนดไว้ล่วงหน้า ดังนั้นคุณจะมีคลาสที่ใช้การพึ่งพา (คลาสนามธรรมหรืออินเทอร์เฟซที่เป็นไปได้มากที่สุด) ที่คุณแกล้งทำเพื่อวัตถุประสงค์ในการทดสอบและวิธีการต่างๆ พวกเขาจะไม่ทำอะไรแฟนซีและคุณจะได้เขียนรหัส stubbed สำหรับมันนอกการทดสอบของคุณ

เยาะเย้ย

การเยาะเย้ยเป็นสิ่งที่เป็นส่วนหนึ่งของการทดสอบของคุณคุณต้องตั้งค่าตามความคาดหวังของคุณ การเยาะเย้ยไม่ได้ตั้งค่าในวิธีที่กำหนดไว้ล่วงหน้าเพื่อให้คุณมีรหัสที่ทำในการทดสอบของคุณ การกำหนด Mocks ในลักษณะที่รันไทม์เนื่องจากรหัสที่กำหนดความคาดหวังต้องทำงานก่อนที่จะทำอะไร

ความแตกต่างระหว่าง Mocks และ Stubs

การทดสอบที่เขียนด้วย mocks มักจะเป็นไปตามinitialize -> set expectations -> exercise -> verifyรูปแบบการทดสอบ initialize -> exercise -> verifyในขณะที่ต้นขั้วที่เขียนไว้ล่วงหน้าจะปฏิบัติตาม

ความคล้ายคลึงกันระหว่าง Mocks และ Stubs

จุดประสงค์ของทั้งคู่คือเพื่อกำจัดการทดสอบการพึ่งพาทั้งหมดของคลาสหรือฟังก์ชันดังนั้นการทดสอบของคุณจะเน้นและเรียบง่ายขึ้นในสิ่งที่พวกเขากำลังพยายามพิสูจน์


876

คำนำ

มีคำจำกัดความของวัตถุหลายอย่างที่ไม่จริง คำทั่วไปคือการทดสอบคู่ นี้รวมระยะ: หุ่น , ปลอม , กุด , เยาะเย้ย

การอ้างอิง

อ้างอิงจากบทความของ Martin Fowler :

  • วัตถุจำลองจะถูกส่งผ่านไป แต่ไม่เคยใช้จริง โดยปกติจะใช้เพื่อเติมรายการพารามิเตอร์
  • วัตถุปลอมมีการใช้งานจริง แต่มักจะใช้ทางลัดบางอย่างซึ่งทำให้พวกเขาไม่เหมาะสำหรับการผลิต (ฐานข้อมูลในหน่วยความจำเป็นตัวอย่างที่ดี)
  • สตับส์ให้คำตอบแบบกระป๋องสำหรับการโทรในระหว่างการทดสอบโดยปกติจะไม่ตอบสนองต่อสิ่งใดนอกสิ่งที่ตั้งโปรแกรมไว้สำหรับการทดสอบ สตับอาจบันทึกข้อมูลเกี่ยวกับการโทรเช่นสตับเกตเวย์อีเมลที่จดจำข้อความที่ 'ส่ง' หรืออาจเป็นเพียงแค่จำนวนข้อความที่ 'ส่ง'
  • Mocksคือสิ่งที่เรากำลังพูดถึงที่นี่: วัตถุตั้งโปรแกรมล่วงหน้าด้วยความคาดหวังซึ่งเป็นรูปแบบของการโทรที่พวกเขาคาดว่าจะได้รับ

สไตล์

Mocks vs Stubs = การทดสอบเชิงพฤติกรรมเทียบกับการทดสอบสถานะ

หลัก

ตามหลักการของการทดสอบเพียงสิ่งเดียวต่อการทดสอบอาจมีหลายสตับในการทดสอบเพียงครั้งเดียว แต่โดยทั่วไปจะมีการจำลองเพียงครั้งเดียว

วงจรชีวิต

ทดสอบวงจรชีวิตด้วยสตับ:

  1. การตั้งค่า - เตรียมวัตถุที่กำลังทดสอบและผู้ทำงานร่วมกันไม่สมบูรณ์
  2. แบบฝึกหัด - ทดสอบการใช้งาน
  3. Verify state - ใช้ asserts เพื่อตรวจสอบสถานะของ object
  4. Teardown - ทำความสะอาดทรัพยากร

ทดสอบวงจรชีวิตด้วย mocks:

  1. ข้อมูลการตั้งค่า - เตรียมวัตถุที่กำลังทดสอบ
  2. การตั้งค่าความคาดหวัง - เตรียมความคาดหวังในการเยาะเย้ยที่ถูกใช้โดยวัตถุหลัก
  3. แบบฝึกหัด - ทดสอบการใช้งาน
  4. ตรวจสอบความคาดหวัง - ตรวจสอบว่ามีการเรียกใช้วิธีการที่ถูกต้องในการจำลอง
  5. Verify state - ใช้ asserts เพื่อตรวจสอบสถานะของ object
  6. Teardown - ทำความสะอาดทรัพยากร

สรุป

ทั้งการทดสอบ mocks และ stubs ให้คำตอบสำหรับคำถาม: ผลลัพธ์คืออะไร

การทดสอบกับ mocks ก็มีความสนใจใน: ผลที่ได้รับทำได้อย่างไร?


เดี๋ยวก่อน mocks ก็กลับคำตอบกระป๋อง? สาเหตุอื่นทำไมพวกเขาตอบคำถาม?
AturSams

จากสิ่งที่คุณเขียนฉันสามารถบอกได้ว่า mocks = stubs + ความคาดหวังและการตรวจสอบเพราะ mocks "ให้คำตอบแบบกระป๋องกับการโทรในระหว่างการทดสอบโดยปกติจะไม่ตอบสนองต่อสิ่งใดนอก และตัวอย่างที่ฟาวเลอร์แสดงให้เห็นว่าเป็นตัวอย่างของต้นขั้วเป็นตัวอย่างของสายลับ! นั่นหมายความว่าการเยาะเย้ยเป็นต้นขั้วและสายลับเป็นต้นขั้ว และต้นขั้วเป็นเพียงวัตถุที่มีวิธีการทำงานหลายอย่าง นอกจากนี้ยังอธิบายว่าทำไม Mockito เลิกใช้วิธีการสตับ ()
kolobok

สิ่งที่ฉันสับสนเกี่ยวกับเรื่องนี้และคำตอบที่ยอมรับได้คือ“ การตั้งค่าความคาดหวัง” สิ่งนี้หมายความว่าอย่างไร โดยปกติใน“ รหัสหลัก” คุณจะสร้างผลลัพธ์ที่คุณคาดหวัง ดูเหมือนว่าคุณใส่ความคาดหวังลงไปในวัตถุล้อเลียนซึ่งไม่เหมาะสมกับฉัน นอกจากนี้คุณสามารถออกกำลังกายเยาะเย้ยได้อย่างง่ายดายด้วยการป้อนข้อมูลบางส่วนจัดเก็บผลลัพธ์สร้าง "ความคาดหวัง" ในภายหลังและเปรียบเทียบ คุณใช้คำศัพท์ที่ฉันคิดว่าเป็นนามธรรมและคลุมเครือเกินไป
IceFire

365

ต้นขั้วเป็นวัตถุปลอมง่าย มันทำให้การทดสอบดำเนินไปอย่างราบรื่น
การเยาะเย้ยเป็นต้นขั้วที่ชาญฉลาด คุณตรวจสอบการทดสอบของคุณผ่านมัน


33
ฉันคิดว่านี่เป็นคำรวบรัดที่สุดและเป็นคำตอบ Takeaway: ต้นขั้วจำลอง IS-A stackoverflow.com/a/17810004/2288628เป็นคำตอบที่ยาวกว่านี้
PoweredByRice

8
ฉันไม่คิดว่าการเยาะเย้ยเป็นต้นขั้ว Mocks ใช้เพื่อยืนยันและไม่ควรส่งคืนข้อมูล stub จะใช้เพื่อส่งคืนข้อมูลและไม่ควรยืนยัน
dave1010

2
@ dave1010 Mocks แน่นอนที่สุดสามารถส่งคืนข้อมูลหรือแม้แต่โยนข้อยกเว้น พวกเขาควรทำเช่นนั้นเพื่อตอบสนองต่อ params ที่ส่งเข้ามา
เทรนตัน

2
@trenton ถ้าวัตถุส่งคืนหรือส่งต่อโดยอิงจากข้อมูลที่ส่งเข้ามานั้นเป็นของปลอมไม่ใช่จำลอง Stubs ทดสอบว่า SUT ของคุณจัดการกับการรับข้อความอย่างไร mocks ทดสอบว่า SUT ของคุณส่งข้อความอย่างไร การผสม 2 อย่างเข้าด้วยกันอาจนำไปสู่การออกแบบ OO ที่ไม่ดี
dave1010

8
ฉันคิดว่านี่ยอดเยี่ยม - ต้นขั้วจะตอบกลับคำถาม ล้อเลียนก็ตอบคำถามเช่นกัน (is-a stub) แต่มันก็ยืนยันว่าคำถามนั้นถูกถาม !!
Leif

238

นี่คือคำอธิบายของแต่ละคนตามด้วยตัวอย่างจริง

  • Dummy - APIเพียงแค่ปลอมค่าเพื่อตอบสนองความ

    ตัวอย่าง : หากคุณกำลังทดสอบวิธีการเรียนที่ต้องการพารามิเตอร์บังคับจำนวนมากในตัวสร้างที่ไม่มีผลต่อการทดสอบของคุณคุณอาจสร้างวัตถุจำลองเพื่อวัตถุประสงค์ในการสร้างอินสแตนซ์ใหม่ของชั้นเรียน

  • ปลอม - สร้างการทดสอบการใช้งานของคลาสที่อาจมีการพึ่งพาโครงสร้างพื้นฐานภายนอกบางอย่าง (เป็นแนวปฏิบัติที่ดีที่การทดสอบหน่วยของคุณไม่ได้มีปฏิสัมพันธ์กับโครงสร้างพื้นฐานภายนอก)

    ตัวอย่าง : สร้างการใช้งานปลอมสำหรับการเข้าถึงฐานข้อมูลแทนที่ด้วยการin-memoryรวบรวม

  • กุด - state-basedวิธีการที่จะกลับมาแทนที่ค่ากำหนดค่าตายตัวยังเรียกว่า

    ตัวอย่าง : คลาสทดสอบของคุณขึ้นอยู่กับวิธีที่Calculate()ใช้เวลาในการทำ 5 นาที แทนที่จะรอ 5 นาทีคุณสามารถแทนที่การใช้งานจริงด้วย stub ที่ส่งคืนค่าฮาร์ดโค้ด ใช้เวลาเพียงเล็กน้อยเท่านั้น

  • เยาะเย้ย - คล้ายกันมากStubแต่interaction-basedไม่ใช่รัฐ ซึ่งหมายความว่าคุณไม่ได้คาดหวังว่าMockจะส่งคืนค่าบางอย่าง แต่ให้ถือว่าลำดับของการเรียกใช้เมธอดเฉพาะ

    ตัวอย่าง: คุณกำลังทดสอบคลาสการลงทะเบียนผู้ใช้ หลังจากที่โทรก็ควรจะเรียกSaveSendConfirmationEmail

StubsและMocksเป็นประเภทย่อยจริง ๆMockทั้งการแลกเปลี่ยนการใช้งานจริงกับการทดสอบการใช้งาน แต่ด้วยเหตุผลที่เฉพาะเจาะจงแตกต่างกัน


175

ในหลักสูตรcodeschool.com การทดสอบ Rails สำหรับ Zombiesพวกเขาให้คำจำกัดความของคำศัพท์นี้:

ต้นขั้ว

สำหรับการแทนที่เมธอดด้วยโค้ดที่ส่งคืนผลลัพธ์ที่ระบุ

เยาะเย้ย

ต้นขั้วที่มีการยืนยันว่าวิธีการที่ได้รับการเรียก

ดังนั้นตามที่ Sean Copenhaver อธิบายไว้ในคำตอบของเขาความแตกต่างคือ mocks สร้างความคาดหวัง (เช่นยืนยันว่าพวกเขาถูกเรียกหรือไม่)


ในการเติมเต็ม Dillon โพสต์คิดเกี่ยวกับเรื่องนี้คุณมีคลาสที่เรียกว่า "MakeACake" ซึ่งมีห้องสมุดหลายแห่ง: นมไข่ไข่น้ำตาลเตาอบ
aarkerio

139

ต้นขั้วไม่ล้มเหลวในการทดสอบของคุณเยาะเย้ย


2
และฉันคิดว่านี่เป็นสิ่งที่ดีคุณรู้ไหมว่าการทดสอบมีพฤติกรรมเดียวกันหลังจากทำการเปลี่ยนโครงสร้างใหม่หรือไม่
RodriKing

1
@RodriKing ฉันมีความรู้สึกเดียวกัน เช่นเดียวกับ Mock การเปลี่ยนแปลงใด ๆ ในรหัสการผลิต - คุณมีการเปลี่ยนแปลงที่สอดคล้องกับรหัสทดสอบ ซึ่งความเจ็บปวดคืออะไร! ด้วย Stubs รู้สึกเหมือนคุณทดสอบพฤติกรรมอยู่เสมอดังนั้นไม่จำเป็นต้องทำการเปลี่ยนแปลงขนาดเล็กด้วยรหัสทดสอบ
tucq88

35

ฉันคิดว่าคำตอบที่ง่ายและชัดเจนที่สุดเกี่ยวกับคำถามนี้ได้รับจากRoy Osheroveในหนังสือของเขาศิลปะการทดสอบหน่วย (หน้า 85)

วิธีที่ง่ายที่สุดที่จะบอกว่าเราติดต่อกับต้นขั้วก็คือการสังเกตว่าต้นขั้วนั้นไม่เคยล้มเหลวในการทดสอบ การยืนยันการทดสอบใช้เสมอกับคลาสที่อยู่ภายใต้การทดสอบ

ในทางกลับกันการทดสอบจะใช้วัตถุจำลองเพื่อตรวจสอบว่าการทดสอบล้มเหลวหรือไม่ [ ... ]

อีกครั้งวัตถุจำลองเป็นวัตถุที่เราใช้เพื่อดูว่าการทดสอบล้มเหลวหรือไม่

นั่นหมายความว่าหากคุณทำการยืนยันกับของปลอมหมายความว่าคุณใช้ของปลอมเป็นล้อเลียนถ้าคุณใช้ของปลอมเท่านั้นเพื่อทำการทดสอบโดยไม่ยืนยันว่าคุณใช้ปลอมเป็นต้นขั้ว


2
ฉันหวังว่าคำตอบของคุณจะหาทางไปด้านบน นี่ของอาร์ Osherove อธิบายนี้youtu.be/fAb_OnooCsQ?t=1006
Michael Ekoka

31

อ่านคำอธิบายทั้งหมดข้างต้นขอผมลองย่อ:

  • Stub : โค้ดหลอก ๆ ที่อนุญาตให้ทำการทดสอบ แต่คุณไม่สนใจว่าจะเกิดอะไรขึ้นกับมัน
  • เยาะเย้ย : ชิ้นส่วนจำลองที่เรียกว่า VERIFY คุณถูกเรียกอย่างถูกต้องว่าเป็นส่วนหนึ่งของการทดสอบ
  • Spy : โค้ดปลอม ๆ ที่ดักจับการเรียกบางส่วนของโค้ดจริงทำให้คุณสามารถตรวจสอบการโทรได้โดยไม่ต้องเปลี่ยนวัตถุเดิมทั้งหมด

4
คำตอบที่ดี. เยาะเย้ยค่อนข้างคล้าย Spy แม้ว่าตามคำจำกัดความของคุณ จะดีถ้าคุณปรับปรุงคำตอบของคุณเพื่อรวมการทดสอบอีกสองสามครั้ง
Rowan Gontier

ฉันไม่เคยได้ยินเรื่อง Spy เมื่อฉันเขียนคำตอบนี้
O'Rooney

23

การเยาะเย้ยเป็นเพียงการทดสอบพฤติกรรมการทำให้แน่ใจว่าวิธีการบางอย่างที่เรียกว่า Stub เป็นรุ่นทดสอบ (ต่อ se) ของวัตถุเฉพาะ

คุณหมายถึงวิธีการของ Apple?


19
"คุณหมายถึงวิธีของ Apple" ใช้ Helvetica
kubi

7
ในแบบของ Apple ซึ่งต่างกับวิธีของ Microsoft :)
never_had_a_name

2
สิ่งนี้ช่วยสถานการณ์ได้หรือไม่?
NebulaFox

21

หากคุณเปรียบเทียบกับการดีบัก:

Stubเป็นเหมือนการทำให้แน่ใจว่าวิธีการส่งกลับค่าที่ถูกต้อง

เยาะเย้ยเป็นจริงก้าวเข้าสู่วิธีการและทำให้แน่ใจว่าทุกอย่างภายในถูกต้องก่อนที่จะส่งกลับค่าที่ถูกต้อง


20

การใช้แบบจำลองทางจิตช่วยให้ฉันเข้าใจสิ่งนี้มากกว่าคำอธิบายและบทความทั้งหมดซึ่งไม่ได้ "จม"

ลองนึกภาพลูกของคุณมีจานแก้ววางอยู่บนโต๊ะและเขาก็เริ่มเล่นกับมัน ตอนนี้คุณกลัวว่ามันจะพัง ดังนั้นคุณให้เขาแผ่นพลาสติกแทน นั่นจะเป็นการเยาะเย้ย (พฤติกรรมเดียวกัน, อินเทอร์เฟซเดียวกัน, การใช้งาน "เบาลง")

ตอนนี้พูดว่าคุณไม่มีแผ่นพลาสติกทดแทนดังนั้นคุณจะอธิบายว่า "ถ้าคุณเล่นต่อไปมันจะพัง!" นั่นคือต้นขั้วคุณได้กำหนดสถานะไว้ล่วงหน้าแล้ว

Dummyจะแยกเขาไม่ได้ใช้ ... และSpyอาจจะมีบางสิ่งบางอย่างเช่นการให้คำอธิบายเดียวกับที่คุณใช้อยู่แล้วที่ทำงาน


19

ฉันคิดว่าความแตกต่างที่สำคัญที่สุดระหว่างพวกเขาคือความตั้งใจของพวกเขา

ให้ฉันพยายามอธิบายในWHY ต้นขั้วและทำไมล้อเลียน

สมมติว่าฉันกำลังเขียนโค้ดทดสอบสำหรับคอนโทรลเลอร์ไทม์ไลน์สาธารณะของไคลเอ็นต์ Twitter ของ Mac

นี่คือตัวอย่างรหัสทดสอบ

twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
  • STUB: การเชื่อมต่อเครือข่ายกับ twitter API ช้ามากซึ่งทำให้การทดสอบของฉันช้า ฉันรู้ว่ามันจะส่งคืนไทม์ไลน์ดังนั้นฉันจึงทำการจำลอง HTTP twitter API ของต้นขั้วเพื่อให้การทดสอบของฉันรันเร็วมากและฉันสามารถรันการทดสอบได้แม้ว่าฉันจะออฟไลน์อยู่
  • จำลอง: ฉันยังไม่ได้เขียนวิธี UI ใด ๆ ของฉันและฉันไม่แน่ใจว่าวิธีการใดที่ฉันต้องเขียนสำหรับวัตถุ UI ของฉัน ฉันหวังว่าจะรู้ว่าตัวควบคุมของฉันจะทำงานร่วมกับวัตถุ UI ของฉันได้อย่างไรโดยการเขียนรหัสทดสอบ

โดยการเขียนจำลองคุณจะค้นพบความสัมพันธ์การทำงานร่วมกันของวัตถุโดยการตรวจสอบความคาดหวังที่จะพบในขณะที่ต้นขั้วเพียงจำลองพฤติกรรมของวัตถุ

ฉันขอแนะนำให้อ่านบทความนี้หากคุณพยายามที่จะรู้เพิ่มเติมเกี่ยวกับ mocks: http://jmock.org/oopsla2004.pdf


1
ฉันคิดว่าคุณมีความคิดที่ถูกต้อง แต่ Dillon Kearns อธิบายอย่างชัดเจนมากขึ้น
O'Rooney

19

เพื่อให้ชัดเจนและใช้งานได้จริง:

Stub: คลาสหรือวัตถุที่ใช้วิธีการของคลาส / วัตถุที่จะแกล้งและส่งคืนสิ่งที่คุณต้องการเสมอ

ตัวอย่างใน JavaScript:

var Stub = {
   method_a: function(param_a, param_b){
      return 'This is an static result';
   }
}

เยาะเย้ย: stub เดียวกัน แต่เพิ่มตรรกะบางอย่างที่ "ตรวจสอบ" เมื่อมีการเรียกวิธีการเพื่อให้คุณมั่นใจได้ว่าการใช้งานบางอย่างเรียกวิธีการนั้น

@mLevan พูดว่าจินตนาการเป็นตัวอย่างที่คุณกำลังทดสอบคลาสการลงทะเบียนผู้ใช้ หลังจากเรียกบันทึกแล้วควรเรียกใช้ SendConfirmationEmail

รหัสโง่มากตัวอย่าง:

var Mock = {
   calls: {
      method_a: 0
   }

   method_a: function(param_a, param_b){
     this.method_a++; 
     console.log('Mock.method_a its been called!');
   }
}

16

สไลด์นี้อธิบายความแตกต่างหลัก ๆ ดีมาก

ป้อนคำอธิบายรูปภาพที่นี่

* จาก CSE 403 บทที่ 16 มหาวิทยาลัยวอชิงตัน (สไลด์ที่สร้างโดย "Marty Stepp")


นี่เป็นคำอธิบายที่ชัดเจนยิ่งขึ้นเกี่ยวกับความแตกต่างระหว่างสอง IMO สำหรับต้นขั้ว: ผู้ทดสอบใช้ต้นขั้วและใช้มันโดยตรงภายในชั้นเรียนภายใต้การทดสอบ แต่สำหรับจำลองผู้ทดสอบจะต้องใช้อุปกรณ์ในการใช้วัตถุจำลอง ในกรณีต่าง ๆ มันจะทำงานต่างกัน ในทางตรงกันข้ามไม่คาดว่าต้นขั้วจะทำงานแตกต่างกัน แต่จะใช้ตามที่เป็นอยู่ (หมายถึงการส่งคืนข้อมูลเดียวกันทุกครั้งที่มีการติดต่อ)
Dexter

12

ฉันชอบ explanantion ใส่ออกโดยรอย Osherove [ลิงค์วิดีโอ]

ทุกคลาสหรือวัตถุที่สร้างขึ้นเป็นของปลอม มันคือการเยาะเย้ยถ้าคุณตรวจสอบการโทรกับมัน มิฉะนั้นมันจะเป็นต้นขั้ว


12
  • Stubs vs. Mocks
    • ไม่สมบูรณ์
      1. ให้คำตอบเฉพาะสำหรับวิธีการโทร
        • ตัวอย่าง: myStubbedService.getValues ​​() เพียงแค่ส่งคืนสตริงที่ต้องการโดยรหัสที่อยู่ในการทดสอบ
      2. ใช้โดยรหัสภายใต้การทดสอบเพื่อแยกมัน
      3. ไม่สามารถทดสอบล้มเหลว
        • เช่น: myStubbedService.getValues ​​() เพียงส่งคืนค่า stubbed
      4. มักใช้วิธีนามธรรม
    • mocks
      1. "superset" ของสตับ สามารถยืนยันได้ว่าวิธีการบางอย่างจะถูกเรียกว่า
        • ตัวอย่าง: ตรวจสอบว่า myMockedService.getValues ​​() ถูกเรียกเพียงครั้งเดียว
      2. ใช้ในการทดสอบพฤติกรรมของรหัสภายใต้การทดสอบ
      3. การทดสอบล้มเหลว
        • ตัวอย่าง: ตรวจสอบว่า myMockedService.getValues ​​() ถูกเรียกครั้งเดียว; การตรวจสอบล้มเหลวเนื่องจาก myMockedService.getValues ​​() ไม่ได้ถูกเรียกโดยรหัสทดสอบของฉัน
      4. มักจะเชื่อมต่อ mocks

11

ให้ดูการทดสอบคู่ผสม:

  • ปลอม : ปลอมเป็นวัตถุที่มีการนำไปใช้งาน แต่ไม่เหมือนกับงานผลิต เช่นการใช้งานในหน่วยความจำของ Data Access Object หรือ Repository
  • Stub : Stub เป็นวัตถุที่เก็บข้อมูลที่กำหนดไว้ล่วงหน้าและใช้เพื่อรับสายระหว่างการทดสอบ เช่น : วัตถุที่ต้องการดึงข้อมูลบางส่วนจากฐานข้อมูลเพื่อตอบสนองต่อการเรียกใช้เมธอด

  • Mocks : Mocks เป็นวัตถุที่ลงทะเบียนการโทรที่พวกเขาได้รับ ในการยืนยันการทดสอบเราสามารถตรวจสอบ Mocks ว่าการกระทำที่คาดหวังทั้งหมดได้ดำเนินการ เช่น : ฟังก์ชันที่เรียกใช้บริการส่งอีเมล นานเพียงตรวจสอบนี้


1
คำตอบที่ดีที่สุดในความคิดของฉัน
Ero Stefano

9

ปลอมเป็นคำทั่วไปที่สามารถนำมาใช้เพื่ออธิบายทั้งต้นขั้วหรือวัตถุจำลอง (เขียนด้วยลายมือหรืออื่น ๆ ) เพราะพวกเขาทั้งสองมีลักษณะเหมือนวัตถุจริง

ไม่ว่าของปลอมจะเป็นต้นขั้วหรือจำลองขึ้นอยู่กับว่ามันถูกใช้ในการทดสอบปัจจุบันหรือไม่ ถ้ามันถูกใช้เพื่อตรวจสอบการโต้ตอบ (ยืนยันกับ) มันเป็นวัตถุจำลอง มิฉะนั้นจะเป็นต้นขั้ว

การปลอมทำให้การทดสอบดำเนินไปอย่างราบรื่น หมายความว่าผู้อ่านการทดสอบในอนาคตของคุณจะเข้าใจสิ่งที่จะเป็นพฤติกรรมของวัตถุปลอมโดยไม่จำเป็นต้องอ่านซอร์สโค้ดของมัน (โดยไม่จำเป็นต้องพึ่งพาทรัพยากรภายนอก)

การทดสอบการทำงานราบรื่นหมายถึงอะไร
ตัวอย่าง Forex ในรหัสด้านล่าง:

 public void Analyze(string filename)
        {
            if(filename.Length<8)
            {
                try
                {
                    errorService.LogError("long file entered named:" + filename);
                }
                catch (Exception e)
                {
                    mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
                }
            }
        }

คุณต้องการทดสอบเมธอด mailService.SendEMail ()เพื่อที่คุณจะต้องทำการจำลองข้อยกเว้นในการทดสอบดังนั้นคุณเพียงแค่สร้างคลาส ErrorService ปลอมของสตับเพื่อจำลองผลลัพธ์นั้นจากนั้นโค้ดทดสอบของคุณจะสามารถทดสอบได้ เมธอด mailService.SendEMail () ตามที่คุณเห็นคุณจำเป็นต้องจำลองผลลัพธ์ซึ่งมาจากคลาส ErrorService ภายนอกการพึ่งพาอื่น


8

ขวาจากMock Rolesกระดาษไม่ใช่ Objectโดยผู้พัฒนา jMock:

Stub คือการใช้งานจริงของรหัสการผลิตที่ส่งคืนผลลัพธ์กระป๋อง วัตถุจำลองทำหน้าที่เหมือนสตับ แต่ยังรวมถึงการยืนยันถึงการโต้ตอบของวัตถุเป้าหมายกับเพื่อนบ้าน

ดังนั้นความแตกต่างที่สำคัญคือ:

  • ความคาดหวังที่ตั้งไว้บนต้นขั้วมักจะเป็นแบบทั่วไปในขณะที่ความคาดหวังที่ตั้งอยู่บน mocks อาจเป็น "ฉลาด" มากกว่า (เช่นส่งคืนนี้ในการโทรครั้งแรกนี่เป็นครั้งที่สอง ฯลฯ )
  • สตับส่วนใหญ่ใช้ในการตั้งค่าอินพุตทางอ้อมของ SUTในขณะที่mocksสามารถใช้ในการทดสอบทั้งอินพุตทางอ้อมและเอาท์พุททางอ้อมของ SUT

เพื่อสรุปผลในขณะที่ยังพยายามที่จะแยกย้ายกันความสับสนจากบทความของพรานล่าสัตว์ชื่อเรื่อง: mocks มีความไม่สมบูรณ์ แต่พวกเขาจะไม่เพียง แต่ไม่สมบูรณ์


1
ฉันคิดว่าคุณพูดถูก แต่นี่คือเหตุผลว่าทำไมบทความ Fowler จึงสับสนชื่อบทความคือ "Mocks Ar't Stubs" ... แต่มันคืออะไร! ¯_ (ツ) _ / ¯
stonedauwg

@stonedauwg แน่นอนฉันแก้ไขโพสต์ของฉันเพื่อรวมปุนและคำชี้แจงของคุณ หวังว่านี่จะช่วยได้มากขึ้น
Dimos

@stonedauwg เยาะเย้ยไม่ได้เป็นต้นขั้วเหมือนสี่เหลี่ยมผืนผ้าไม่ใช่สี่เหลี่ยม :)
seanriordan08

7

ฉันกำลังอ่านThe Art of Unit Testingและสะดุดกับคำจำกัดความต่อไปนี้:

ปลอมเป็นคำทั่วไปที่สามารถนำมาใช้เพื่ออธิบายทั้งต้นขั้วหรือวัตถุจำลอง (เขียนด้วยลายมือหรืออื่น ๆ ) เพราะพวกเขาทั้งสองมีลักษณะเหมือนวัตถุจริง ไม่ว่าของปลอมจะเป็นต้นขั้วหรือจำลองขึ้นอยู่กับว่ามันถูกใช้ในการทดสอบปัจจุบันหรือไม่ ถ้ามันใช้ในการตรวจสอบการปฏิสัมพันธ์ (ถูกกล่าวหาผิด) มันเป็นวัตถุจำลอง มิฉะนั้นจะเป็นต้นขั้ว


5

ฉันมาข้ามบทความที่น่าสนใจนี้โดย UncleBob The Little เยาะเย้ย มันอธิบายคำศัพท์ทั้งหมดในลักษณะที่เข้าใจง่ายดังนั้นจึงมีประโยชน์สำหรับผู้เริ่มต้น บทความ Martin Fowlers อ่านยากโดยเฉพาะสำหรับผู้เริ่มต้นอย่างฉัน


4

Stubช่วยให้เราสามารถทำการทดสอบ อย่างไร? มันให้ค่าที่ช่วยในการรันการทดสอบ ค่าเหล่านี้ไม่ได้เกิดขึ้นจริงและเราสร้างค่าเหล่านี้เพื่อทำการทดสอบ ตัวอย่างเช่นเราสร้าง HashMap เพื่อให้คุณค่ากับเราซึ่งคล้ายกับค่าในตารางฐานข้อมูล ดังนั้นแทนที่จะโต้ตอบกับฐานข้อมูลโดยตรงเราจึงโต้ตอบกับ Hashmap

จำลองเป็นวัตถุปลอมที่รันการทดสอบ ที่เราใส่ยืนยัน


"ดังนั้นแทนที่จะโต้ตอบกับฐานข้อมูลโดยตรงเราจึงโต้ตอบกับ Hashmap" ... เพราะเมื่อตอนนี้ยังไม่มีรหัสฐานข้อมูลและเราไม่สามารถรันโค้ดทดสอบได้โดยไม่ต้องใช้ stub ไม่เช่นนั้น Hasmap ที่เหมือนกันมาก ๆ ก็คงจะเป็นการล้อเลียน ขวา?
Boris Däppen

4

ดูตัวอย่างด้านล่างของ mocks vs stubs โดยใช้ C # และ Moq framework Moq ไม่มีคำหลักพิเศษสำหรับ Stub แต่คุณสามารถใช้ Mock object เพื่อสร้าง stub ได้เช่นกัน

namespace UnitTestProject2
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using Moq;
    [TestClass]
    public class UnitTest1
    {
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));

            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
        }
        /// <summary>
        /// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
        {
            // Arrange 
            var mockEntityRepository = new Mock<IEntityRepository>();
            mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
            var entity = new EntityClass(mockEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(0);
            // Assert
            mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
        }
        /// <summary>
        /// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
        /// </summary>
        [TestMethod]
        public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
        {
            // Arrange 
            var stubEntityRepository = new Mock<IEntityRepository>();
            stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
                .Returns("Stub");
            const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
            var entity = new EntityClass(stubEntityRepository.Object);
            // Act 
            var name = entity.GetNameWithPrefix(12);
            // Assert
            Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
        }
    }
    public class EntityClass
    {
        private IEntityRepository _entityRepository;
        public EntityClass(IEntityRepository entityRepository)
        {
            this._entityRepository = entityRepository;
        }
        public string Name { get; set; }
        public string GetNameWithPrefix(int id)
        {
            string name = string.Empty;
            if (id > 0)
            {
                name = this._entityRepository.GetName(id);
            }
            return "Mr. " + name;
        }
    }
    public interface IEntityRepository
    {
        string GetName(int id);
    }
    public class EntityRepository:IEntityRepository
    {
        public string GetName(int id)
        {
            // Code to connect to DB and get name based on Id
            return "NameFromDb";
        }
    }
}

4

มุมมองการทดสอบ Stub และ Mock:

  • Stubคือการนำดัมมี่ไปใช้โดยผู้ใช้ด้วยวิธีสแตติกหมายถึงใน Stub ที่เขียนโค้ดการใช้งาน ดังนั้นจึงไม่สามารถจัดการคำจำกัดความของบริการและเงื่อนไขแบบไดนามิกได้โดยปกติจะทำในกรอบงาน JUnit โดยไม่ต้องใช้กรอบการเยาะเย้ย

  • จำลองนั้นยังใช้งานแบบจำลองได้ แต่การนำไปใช้งานทำได้อย่างไดนามิกโดยใช้ Mocking frameworks เช่น Mockito ดังนั้นเราสามารถจัดการเงื่อนไขและคำจำกัดความการบริการเป็นวิธีแบบไดนามิกเช่น mocks สามารถสร้างแบบไดนามิกจากรหัสที่รันไทม์ ดังนั้นการใช้การเยาะเย้ยเราสามารถใช้ Stubs แบบไดนามิก


3

บวกกับคำตอบที่เป็นประโยชน์หนึ่งในจุดที่มีประสิทธิภาพที่สุดในการใช้ Mocks กว่า Subs

ถ้าทำงานร่วมกัน [ซึ่งรหัสหลักขึ้นอยู่กับมัน] ไม่อยู่ภายใต้การควบคุมของเรา (เช่นจากห้องสมุดของบุคคลที่สาม)
ในกรณีนี้ต้นขั้วเป็นเรื่องยากมากที่จะเขียนมากกว่าการเยาะเย้ย


2

ฉันใช้ตัวอย่างไพ ธ อนในคำตอบเพื่อแสดงความแตกต่าง

Stub - Stubbing เป็นเทคนิคการพัฒนาซอฟต์แวร์ที่ใช้ในการใช้วิธีการเรียนในช่วงต้นของวงจรการพัฒนา โดยทั่วไปจะใช้เป็นตัวยึดตำแหน่งสำหรับการใช้งานอินเทอร์เฟซที่รู้จักซึ่งอินเตอร์เฟสจะถูกสรุปหรือรู้จัก แต่การนำไปใช้ยังไม่เป็นที่รู้จัก คุณเริ่มต้นด้วย stubs ซึ่งหมายความว่าคุณเพียงเขียนคำจำกัดความของฟังก์ชั่นลงและปล่อยให้รหัสจริงในภายหลัง ข้อได้เปรียบคือคุณจะไม่ลืมวิธีการและคุณสามารถคิดเกี่ยวกับการออกแบบของคุณในขณะที่เห็นในรหัส คุณสามารถให้ stub ของคุณคืนการตอบสนองแบบสแตติกเพื่อให้สามารถใช้การตอบกลับโดยส่วนอื่น ๆ ของรหัสของคุณได้ทันที วัตถุ Stub ให้การตอบสนองที่ถูกต้อง แต่มันคงที่ไม่ว่าคุณจะป้อนข้อมูลใดก็ตามคุณจะได้รับการตอบกลับเหมือนเดิมเสมอ:

class Foo(object):
    def bar1(self):
        pass

    def bar2(self):
        #or ...
        raise NotImplementedError

    def bar3(self):
        #or return dummy data
        return "Dummy Data"

วัตถุจำลองถูกนำมาใช้ในกรณีทดสอบจำลองซึ่งตรวจสอบความถูกต้องของวิธีการบางอย่างบนวัตถุเหล่านั้น วัตถุจำลองเป็นวัตถุจำลองที่เลียนแบบพฤติกรรมของวัตถุจริงด้วยวิธีการควบคุม โดยทั่วไปคุณสร้างวัตถุจำลองเพื่อทดสอบพฤติกรรมของวัตถุอื่น ๆ Mocks ให้เราจำลองทรัพยากรที่ไม่พร้อมใช้งานหรือไม่สะดวกสำหรับการทดสอบหน่วย

mymodule.py:

import os
import os.path

def rm(filename):
    if os.path.isfile(filename):
        os.remove(filename)

test.py:

from mymodule import rm
import mock
import unittest

class RmTestCase(unittest.TestCase):
    @mock.patch('mymodule.os')
    def test_rm(self, mock_os):
        rm("any path")
        # test that rm called os.remove with the right parameters
        mock_os.remove.assert_called_with("any path")

if __name__ == '__main__':
    unittest.main()

นี่เป็นตัวอย่างพื้นฐานที่เพิ่งรัน rm และยืนยันพารามิเตอร์ที่ถูกเรียกด้วย คุณสามารถใช้การเยาะเย้ยกับวัตถุไม่ใช่แค่ฟังก์ชั่นดังที่แสดงไว้ที่นี่และคุณยังสามารถคืนค่าเพื่อให้วัตถุจำลองสามารถใช้แทนต้นขั้วสำหรับการทดสอบได้

เพิ่มเติมเกี่ยวกับunittest.mockหมายเหตุใน python 2.x mock ไม่ได้รวมอยู่ใน unittest แต่เป็นโมดูลที่สามารถดาวน์โหลดได้ซึ่งสามารถดาวน์โหลดผ่าน pip (mock pip install mock)

ฉันยังได้อ่าน "ศิลปะแห่งการทดสอบหน่วย" โดย Roy Osherove และฉันคิดว่ามันจะดีถ้าหนังสือที่คล้ายกันเขียนขึ้นโดยใช้ตัวอย่าง Python และ Python หากใครรู้หนังสือเล่มนี้โปรดแชร์ ไชโย :)


2

ต้นขั้วเป็นวัตถุปลอมสร้างขึ้นเพื่อวัตถุประสงค์ในการทดสอบ mock คือ stub ที่บันทึกว่าการโทรที่คาดไว้เกิดขึ้นอย่างมีประสิทธิภาพหรือไม่


2

Stub เป็นฟังก์ชันว่างซึ่งใช้เพื่อหลีกเลี่ยงข้อยกเว้นที่ไม่สามารถจัดการได้ในระหว่างการทดสอบ:

function foo(){}

จำลองเป็นฟังก์ชั่นประดิษฐ์ที่ใช้เพื่อหลีกเลี่ยงระบบปฏิบัติการสภาพแวดล้อมหรือการพึ่งพาฮาร์ดแวร์ในระหว่างการทดสอบ:

function foo(bar){ window = this; return window.toString(bar); }

ในแง่ของการยืนยันและรัฐ:

  • Mocks ถูกยืนยันก่อนเหตุการณ์หรือการเปลี่ยนแปลงสถานะ
  • Stubs ไม่ได้ถูกยืนยันพวกเขาให้สถานะก่อนเหตุการณ์เพื่อหลีกเลี่ยงการเรียกใช้โค้ดจากหน่วยที่ไม่เกี่ยวข้อง
  • สายลับถูกตั้งค่าเช่นต้นขั้วแล้วยืนยันหลังจากเหตุการณ์หรือการเปลี่ยนแปลงสถานะ
  • ของปลอมไม่ได้ถูกยืนยันพวกเขาทำงานหลังจากเหตุการณ์ที่มีการอ้างอิง hardcoded เพื่อหลีกเลี่ยงสถานะ

อ้างอิง


2
+1 สำหรับเพิ่มสายลับในอภิธานศัพท์ นอกจากนี้ฉันคิดว่าคุณหมายถึง "สายลับกำลังเซ็ตอัพเหมือน mocks" ไม่ใช่ "สายลับกำลังเซ็ตอัพเหมือนต้นขั้ว"
Sameh Deabes


2

การเยาะเย้ยเป็นทั้งด้านเทคนิคและวัตถุที่ใช้งานได้

จำลองเป็นทางด้านเทคนิค มันถูกสร้างขึ้นแน่นอนโดยห้องสมุดเยาะเย้ย (EasyMock, JMockit และเมื่อเร็ว ๆ Mockito เป็นที่รู้จักสำหรับเหล่านี้) ขอบคุณที่สร้างรหัสไบต์
การใช้งานจำลองถูกสร้างขึ้นในวิธีที่เราสามารถใช้มันในการส่งกลับค่าที่เฉพาะเจาะจงเมื่อมีการเรียกวิธีการ แต่ยังมีสิ่งอื่น ๆ เช่นการตรวจสอบว่าวิธีการเยาะเย้ยถูกเรียกด้วยพารามิเตอร์เฉพาะบางอย่าง ไม่มีการตรวจสอบอย่างเข้มงวด)

ยกตัวอย่างเยาะเย้ย:

@Mock Foo fooMock

การบันทึกพฤติกรรม:

when(fooMock.hello()).thenReturn("hello you!");

การตรวจสอบการร้องขอ:

verify(fooMock).hello()

เห็นได้ชัดว่าไม่ใช่วิธีที่เป็นธรรมชาติในการสร้างอินสแตนซ์ / แทนที่คลาส / พฤติกรรมของฟู นั่นเป็นเหตุผลที่ฉันอ้างถึงด้านเทคนิค

แต่การเยาะเย้ยนั้นก็ใช้งานได้เพราะมันเป็นตัวอย่างของคลาสที่เราต้องแยกออกจาก SUT และด้วยพฤติกรรมที่บันทึกไว้ในนั้นเราสามารถใช้มันใน SUT ด้วยวิธีเดียวกันกับที่เราจะทำกับต้นขั้ว


stub เป็นเพียงวัตถุที่ใช้งานได้: นั่นเป็นตัวอย่างของคลาสที่เราต้องแยกออกจาก SUT และนั่นคือทั้งหมด นั่นหมายถึงว่าต้องมีการกำหนดทั้งส่วนต้นขั้วและพฤติกรรมทั้งหมดที่จำเป็นในระหว่างการทดสอบหน่วยของเราอย่างชัดเจน
ตัวอย่างเช่น stub hello()จะต้อง subclass Fooclass (หรือใช้ส่วนต่อประสานกับมัน) และแทนที่hello() :

public class HelloStub extends Hello{    
  public String hello { 
      return "hello you!"; 
  }
}

หากสถานการณ์จำลองการทดสอบอื่นต้องการผลตอบแทนเป็นค่าอื่นเราอาจต้องกำหนดวิธีทั่วไปในการตั้งค่าผลตอบแทน:

public class HelloStub extends Hello{    
  public HelloStub(String helloReturn){
       this.helloReturn = helloReturn;
  }
  public String hello { 
      return helloReturn; 
  }
}

สถานการณ์อื่น ๆ : ถ้าฉันมีวิธีผลข้างเคียง (ไม่กลับมา) และฉันจะตรวจสอบว่าวิธีการที่ถูกเรียกฉันอาจจะต้องเพิ่มบูลีนหรือเคาน์เตอร์ในชั้นต้นขั้วเพื่อนับจำนวนวิธีที่ถูกเรียก


ข้อสรุป

Stub ต้องการค่าใช้จ่าย / รหัสจำนวนมากในการเขียนเพื่อทดสอบหน่วยของคุณ สิ่งที่เยาะเย้ยป้องกันไม่ให้ขอบคุณที่ให้การบันทึก / ตรวจสอบคุณสมบัติออกจากกล่อง
นั่นเป็นเหตุผลที่ทุกวันนี้วิธีการสตับนั้นไม่ค่อยได้ใช้ในทางปฏิบัติกับการมีห้องสมุดจำลองที่ยอดเยี่ยม


เกี่ยวกับบทความ Martin Fowler: ฉันไม่คิดว่าจะเป็นโปรแกรมเมอร์ "ผู้เยาะเย้ย" ในขณะที่ฉันใช้ mocks และฉันหลีกเลี่ยงที่ไม่สมบูรณ์
แต่ฉันใช้จำลองเมื่อจำเป็นจริงๆ (การอ้างอิงที่น่ารำคาญ) และฉันชอบการทดสอบการตัดและการทดสอบการรวมขนาดเล็กเมื่อฉันทดสอบคลาสที่มีการอ้างอิงซึ่งการเยาะเย้ยจะเป็นค่าใช้จ่าย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.