mockito เมื่อ () การเรียกใช้ทำงานอย่างไร


111

ให้คำสั่ง Mockito ต่อไปนี้:

when(mock.method()).thenReturn(someValue);

Mockito ไปเกี่ยวกับการสร้างสิ่งที่เป็นพร็อกซีสำหรับการจำลองอย่างไรเนื่องจากคำสั่ง mock.method () จะส่งค่าส่งคืนไปยังเมื่อ ()? ฉันจินตนาการว่าสิ่งนี้ใช้ CGLib บางอย่าง แต่สนใจที่จะรู้ว่าวิธีนี้ทำได้อย่างไร

คำตอบ:


118

คำตอบสั้น ๆ คือในตัวอย่างของคุณผลลัพธ์ของmock.method()จะเป็นค่าว่างที่เหมาะสมกับประเภท mockito ใช้การกำหนดทิศทางผ่านทางพร็อกซีการสกัดกั้นเมธอดและอินสแตนซ์ที่ใช้ร่วมกันของMockingProgressคลาสเพื่อพิจารณาว่าการเรียกใช้เมธอดในการจำลองนั้นใช้สำหรับการทำให้เป็นจุดบอดหรือเล่นซ้ำของพฤติกรรมที่ถูกตรึงที่มีอยู่แทนที่จะส่งข้อมูลเกี่ยวกับการแตกผ่านค่าส่งคืนของ วิธีการเยาะเย้ย

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

ขั้นแรกเมื่อคุณล้อเลียนชั้นเรียนโดยใช้mockวิธีการของMockitoชั้นเรียนนี่คือสิ่งที่เกิดขึ้นโดยพื้นฐาน:

  1. Mockito.mockมอบหมายให้. org.mockito.internal.MockitoCoreม็อคส่งผ่านการตั้งค่าการจำลองเริ่มต้นเป็นพารามิเตอร์
  2. MockitoCore.mockมอบหมายให้ . org.mockito.internal.util.MockUtilcreateMock
  3. MockUtilระดับใช้ClassPathLoaderระดับที่จะได้รับตัวอย่างของMockMakerการใช้ในการสร้างจำลอง โดยค่าเริ่มต้นคลาสCgLibMockMakerจะถูกใช้
  4. CgLibMockMakerใช้คลาสที่ยืมมาจาก JMock ClassImposterizerซึ่งจัดการกับการสร้างแบบจำลอง ชิ้นสำคัญของ 'Mockito มายากล' ใช้เป็นMethodInterceptorใช้ในการสร้างจำลอง: Mockito MethodInterceptorFilterและห่วงโซ่ของอินสแตนซ์ MockHandler รวมทั้งตัวอย่างของMockHandlerImpl ตัวสกัดกั้นเมธอดจะส่งการวิงวอนไปยังอินสแตนซ์ MockHandlerImpl ซึ่งใช้ตรรกะทางธุรกิจที่ควรใช้เมื่อมีการเรียกใช้เมธอดในการจำลอง (เช่นการค้นหาเพื่อดูว่ามีการบันทึกคำตอบไว้แล้วหรือไม่โดยพิจารณาว่าการเรียกใช้แทนต้นขั้วใหม่หรือไม่เป็นต้น สถานะดีฟอลต์คือถ้าไม่ได้ลงทะเบียนต้นขั้วสำหรับเมธอดที่เรียกใช้ค่าว่างที่เหมาะสมกับประเภทจะถูกส่งกลับ

ตอนนี้มาดูโค้ดในตัวอย่างของคุณ:

when(mock.method()).thenReturn(someValue)

นี่คือลำดับที่รหัสนี้จะดำเนินการใน:

  1. mock.method()
  2. when(<result of step 1>)
  3. <result of step 2>.thenReturn

กุญแจสำคัญในการทำความเข้าใจสิ่งที่เกิดขึ้นเป็นสิ่งที่เกิดขึ้นเมื่อวิธีการในการจำลองที่ถูกเรียก: วิธีการสกัดกั้นจะถูกส่งข้อมูลเกี่ยวกับวิธีการอุทธรณ์และได้รับมอบหมายให้ห่วงโซ่ของอินสแตนซ์ซึ่งท้ายที่สุดก็มอบหมายให้MockHandler MockHandlerImpl#handleในระหว่างMockHandlerImpl#handleนั้นตัวจัดการจำลองจะสร้างอินสแตนซ์OngoingStubbingImplและส่งต่อไปยังMockingProgressอินสแตนซ์ที่แชร์

เมื่อwhenเมธอดถูกเรียกใช้หลังจากการเรียกใช้เมธอดmethod()นั้นจะมอบหมายให้MockitoCore.whenซึ่งเรียกใช้stub()เมธอดของคลาสเดียวกัน วิธีนี้จะคลายการแยกส่วนที่ต่อเนื่องออกจากMockingProgressอินสแตนซ์ที่ใช้ร่วมกันที่method()คำเรียกร้องจำลองเขียนลงไป จากนั้นthenReturnจึงเรียกวิธีการบนOngoingStubbingอินสแตนซ์


1
ขอบคุณสำหรับคำตอบโดยละเอียด คำถามอื่น - คุณพูดถึงว่า "เมื่อมีการเรียกใช้เมธอดหลังจากการเรียกใช้เมธอด ()" - จะรู้ได้อย่างไรว่าการเรียกใช้เมื่อ () เป็นการเรียกครั้งต่อไป (หรือตัด) การเรียกใช้เมธอด ()? หวังว่าจะสมเหตุสมผล
มีนาคม

@marchaos มันไม่รู้. ด้วยwhen(mock.method()).thenXyz(...)ไวยากรณ์mock.method()จะได้รับการดำเนินการในโหมด "เล่นซ้ำ" ไม่ใช่ในโหมด "สไตรค์" โดยปกติแล้วการดำเนินการนี้mock.method()จะไม่มีผลดังนั้นต่อมาเมื่อthenXyz(...)( thenReturn, thenThrow, thenAnswerฯลฯ ) ได้รับการดำเนินการก็จะเข้าสู่ "stubbing โหมด" แล้วบันทึกผลที่ต้องการสำหรับการโทรวิธีการที่
Rogério

1
Rogerio จริงๆแล้วมันมีความละเอียดอ่อนกว่านั้นเล็กน้อย - mockito ไม่มีโหมด Stubbing และเล่นซ้ำอย่างชัดเจน ฉันจะแก้ไขคำตอบในภายหลังเพื่อให้ชัดเจนยิ่งขึ้น
Paul Morie

ฉันจะสรุปสั้น ๆ ว่ามันง่ายกว่าที่จะดักฟังการเรียกเมธอดภายในเมธอดอื่นด้วย CGLIB หรือ Javassist ที่จะดักฟังพูดว่าโอเปอเรเตอร์ "if"
Infeligo

ฉันยังไม่ได้นวดคำอธิบายของฉันที่นี่ แต่ฉันก็ยังไม่ลืมเรื่องนี้เช่นกัน FYI.
Paul Morie

33

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

ฉันพบว่าบทความนี้มีประโยชน์มาก: คำอธิบายวิธีการทำงานของ Mock Frameworks ที่ใช้พร็อกซี ( http://blog.rseiler.at/2014/06/explanation-how-proxy-based-mock.html ) ผู้เขียนใช้กรอบการจำลองการสาธิตซึ่งฉันพบว่าเป็นแหล่งข้อมูลที่ดีมากสำหรับผู้ที่ต้องการทราบว่ากรอบการจำลองเหล่านี้ทำงานอย่างไร

ในความคิดของฉันมันเป็นการใช้ Anti-Pattern ทั่วไป โดยปกติเราควรหลีกเลี่ยง 'ผลข้างเคียง' เมื่อเราใช้วิธีการซึ่งหมายความว่าเมธอดควรยอมรับอินพุตและทำการคำนวณและส่งคืนผลลัพธ์ - ไม่มีอะไรเปลี่ยนแปลงนอกเหนือจากนั้น แต่ Mockito เพียงแค่ละเมิดกฎนั้นโดยเจตนา วิธีการจัดเก็บข้อมูลมากมายนอกเหนือจากการส่งคืนผลลัพธ์: Mockito.anyString (), mockInstance.method (), when (), thenReturn, พวกเขาทั้งหมดมี 'ผลข้างเคียง' พิเศษ นั่นเป็นเหตุผลว่าทำไมเฟรมเวิร์กจึงดูเหมือนเวทมนตร์เมื่อมองแวบแรกเรามักจะไม่เขียนโค้ดแบบนั้น อย่างไรก็ตามในกรณีของกรอบการเยาะเย้ยการออกแบบต่อต้านรูปแบบนี้เป็นการออกแบบที่ยอดเยี่ยมเนื่องจากนำไปสู่ ​​API ที่เรียบง่ายมาก


4
ลิงค์ที่ยอดเยี่ยม อัจฉริยะที่อยู่เบื้องหลังสิ่งนี้คือ: API ที่เรียบง่ายซึ่งทำให้ทุกอย่างดูดีมาก การตัดสินใจที่ดีอีกประการหนึ่งคือเมธอด when () ใช้ generics เพื่อให้เมธอด thenReturn () เป็นประเภทที่ปลอดภัย
David Tonhofer

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