ปรับปรุงกันยายน 2019:เพียงกรอบเยาะเย้ยการสนับสนุน (ค่าเริ่มต้น) โดยฤดูใบไม้ผลิ Bootเป็นMockito หากคุณใช้ Spring คำตอบค่อนข้างชัดเจน
ผมว่าการแข่งขันอยู่ระหว่างJMockitและPowerMockแล้วMockito
ฉันจะปล่อย jMock และ EasyMock แบบ "ธรรมดา" เพราะใช้เพียงพร็อกซีและ CGLIB และไม่ใช้เครื่องมือวัด Java 5 เหมือนเฟรมเวิร์กรุ่นใหม่
jMock ยังไม่มีรุ่นที่เสถียรมานานกว่า 4 ปี jMock 2.6.0 ต้องใช้เวลา 2 ปีในการเปลี่ยนจาก RC1 เป็น RC2 และอีก 2 ปีก่อนที่จะวางจำหน่ายจริง
เกี่ยวกับ Proxy & CGLIB เทียบกับเครื่องมือวัด:
(EasyMock และ jMock) ขึ้นอยู่กับ java.lang.reflect.Proxy ซึ่งต้องใช้อินเทอร์เฟซในการใช้งาน นอกจากนี้ยังสนับสนุนการสร้างอ็อบเจ็กต์จำลองสำหรับคลาสผ่านการสร้างคลาสย่อย CGLIB ด้วยเหตุนี้คลาสดังกล่าวจึงไม่สามารถเป็นขั้นสุดท้ายได้และสามารถเยาะเย้ยเมธอดอินสแตนซ์ที่สามารถลบล้างได้เท่านั้น อย่างไรก็ตามสิ่งสำคัญที่สุดคือเมื่อใช้เครื่องมือเหล่านี้การอ้างอิงของโค้ดภายใต้การทดสอบ (นั่นคืออ็อบเจ็กต์ของคลาสอื่น ๆ ที่คลาสที่กำหนดภายใต้การทดสอบขึ้นอยู่) จะต้องได้รับการควบคุมโดยการทดสอบเพื่อให้สามารถส่งอินสแตนซ์จำลองไปยังไคลเอนต์ได้ ของการอ้างอิงเหล่านั้น ดังนั้นการอ้างอิงจึงไม่สามารถสร้างอินสแตนซ์กับตัวดำเนินการใหม่ในคลาสไคลเอนต์ที่เราต้องการเขียนการทดสอบหน่วยได้
ในที่สุดข้อ จำกัด ทางเทคนิคของเครื่องมือจำลองแบบทั่วไปกำหนดข้อ จำกัด การออกแบบต่อไปนี้สำหรับรหัสการผลิต:
- แต่ละคลาสที่อาจต้องจำลองในการทดสอบจะต้องใช้อินเทอร์เฟซแยกต่างหากหรือไม่ถือเป็นที่สิ้นสุด
- การอ้างอิงของแต่ละคลาสที่จะทดสอบต้องได้รับผ่านวิธีการสร้างอินสแตนซ์ที่กำหนดค่าได้ (โรงงานหรือตัวระบุตำแหน่งบริการ) หรือถูกเปิดเผยสำหรับการฉีดแบบพึ่งพา มิฉะนั้นการทดสอบหน่วยจะไม่สามารถส่งผ่านการจำลองการใช้การอ้างอิงไปยังหน่วยที่กำลังทดสอบได้
- เนื่องจากสามารถเยาะเย้ยเมธอดอินสแตนซ์ได้เท่านั้นคลาสที่จะทดสอบหน่วยจึงไม่สามารถเรียกเมธอดแบบคงที่ใด ๆ ตามการอ้างอิงหรือสร้างอินสแตนซ์โดยใช้ตัวสร้างใด ๆ
ดังกล่าวข้างต้นจะถูกคัดลอกจากhttp://jmockit.org/about.html นอกจากนี้ยังเปรียบเทียบระหว่างตัวมันเอง (JMockit), PowerMock และ Mockito ได้หลายวิธี:
ขณะนี้มีเครื่องมือจำลองอื่น ๆ สำหรับ Java ซึ่งสามารถเอาชนะข้อ จำกัด ของเครื่องมือทั่วไปได้เช่น PowerMock, jEasyTest และ MockInject สิ่งที่ใกล้เคียงที่สุดกับชุดคุณลักษณะของ JMockit คือ PowerMock ดังนั้นฉันจะประเมินสั้น ๆ ที่นี่ (นอกจากนี้อีกสองรายการมีข้อ จำกัด มากกว่าและดูเหมือนจะไม่ได้รับการพัฒนาอย่างจริงจังอีกต่อไป)
JMockit กับ PowerMock
- ประการแรก PowerMock ไม่มี API ที่สมบูรณ์สำหรับการจำลอง แต่ทำงานเป็นส่วนขยายของเครื่องมืออื่นแทนซึ่งปัจจุบันอาจเป็น EasyMock หรือ Mockito เห็นได้ชัดว่านี่เป็นข้อดีสำหรับผู้ใช้เครื่องมือเหล่านั้นที่มีอยู่
- ในทางกลับกัน JMockit มี API ใหม่ทั้งหมดแม้ว่า API หลัก (Expectations) จะคล้ายกับ EasyMock และ jMock แม้ว่าสิ่งนี้จะสร้างเส้นโค้งการเรียนรู้ที่ยาวขึ้น แต่ก็ยังช่วยให้ JMockit สามารถจัดเตรียม API ที่เรียบง่ายสอดคล้องกันมากขึ้นและใช้งานง่ายขึ้น
- เมื่อเทียบกับ JMockit Expectations API แล้ว PowerMock API จะเป็น "ระดับต่ำ" มากกว่าโดยบังคับให้ผู้ใช้ทราบและระบุว่าต้องเตรียมคลาสใดสำหรับการทดสอบ (ด้วยคำอธิบายประกอบ @PrepareForTest ({ClassA.class, ... }) ) และต้องการการเรียก API เฉพาะเพื่อจัดการกับโครงสร้างภาษาประเภทต่างๆที่อาจมีอยู่ในรหัสการผลิต: วิธีการแบบคงที่ (mockStatic (ClassA.class)), ตัวสร้าง (ปราบปราม (ตัวสร้าง (ClassXyz.class))), การเรียกตัวสร้าง ( expectNew (AClass.class)), mocks บางส่วน (createPartialMock (ClassX.class, "methodToMock")) ฯลฯ
- ด้วยความคาดหวังของ JMockit วิธีการและตัวสร้างทุกประเภทจะถูกล้อเลียนในลักษณะที่เปิดเผยอย่างหมดจดโดยมีการล้อเลียนบางส่วนที่ระบุผ่านนิพจน์ทั่วไปในคำอธิบายประกอบ @Mocked หรือเพียงแค่ "ไม่ล้อเลียน" สมาชิกโดยไม่มีความคาดหวังที่บันทึกไว้ นั่นคือผู้พัฒนาเพียงแค่ประกาศ "ช่องจำลอง" ที่ใช้ร่วมกันสำหรับคลาสทดสอบหรือ "ฟิลด์จำลองในพื้นที่" และ / หรือ "พารามิเตอร์จำลอง" สำหรับวิธีการทดสอบแต่ละวิธี (และในกรณีสุดท้ายนี้คำอธิบายประกอบ @Mocked มักจะไม่ จำเป็น)
- ความสามารถบางอย่างที่มีอยู่ใน JMockit เช่นการรองรับการเยาะเย้ยเท่ากับและ hashCode วิธีการที่ถูกลบล้างและอื่น ๆ ไม่ได้รับการสนับสนุนใน PowerMock นอกจากนี้ยังไม่มีความสามารถเทียบเท่ากับความสามารถของ JMockit ในการจับภาพอินสแตนซ์และจำลองการใช้งานประเภทพื้นฐานที่ระบุในขณะที่การทดสอบดำเนินการโดยที่โค้ดทดสอบจะไม่มีความรู้เกี่ยวกับคลาสการใช้งานจริง
- PowerMock ใช้ตัวโหลดคลาสแบบกำหนดเอง (โดยปกติคือหนึ่งตัวต่อคลาสทดสอบ) เพื่อสร้างคลาสจำลองที่ถูกจำลอง การใช้ตัวโหลดคลาสแบบกำหนดเองอย่างหนักเช่นนี้อาจนำไปสู่ความขัดแย้งกับไลบรารีของบุคคลที่สามดังนั้นบางครั้งจึงจำเป็นต้องใช้คำอธิบายประกอบ @PowerMockIgnore ("package.to.be.ignored") ในคลาสทดสอบ
- กลไกที่ใช้โดย JMockit (เครื่องมือวัดรันไทม์ผ่าน "Java agent") นั้นง่ายและปลอดภัยกว่าแม้ว่าจะต้องส่งผ่านพารามิเตอร์ "-javaagent" ไปยัง JVM เมื่อพัฒนาบน JDK 1.5; บน JDK 1.6+ (ซึ่งสามารถใช้ในการพัฒนาได้ตลอดเวลาแม้ว่าจะปรับใช้กับเวอร์ชันเก่ากว่าก็ตาม) ไม่มีข้อกำหนดดังกล่าวเนื่องจาก JMockit สามารถโหลดเอเจนต์ Java ได้อย่างโปร่งใสตามต้องการโดยใช้ Attach API
เครื่องมือจำลองล่าสุดอีกอย่างคือ Mockito แม้ว่าจะไม่พยายามเอาชนะข้อ จำกัด ของเครื่องมือรุ่นเก่า (jMock, EasyMock) แต่ก็มีการทดสอบพฤติกรรมรูปแบบใหม่ด้วยการล้อเลียน JMockit ยังสนับสนุนรูปแบบทางเลือกนี้ผ่าน Verifications API
JMockit กับ Mockito
- Mockito อาศัยการเรียกที่ชัดเจนไปยัง API เพื่อแยกรหัสระหว่างขั้นตอนการบันทึก (เมื่อ (... )) และขั้นตอนการตรวจสอบ (ตรวจสอบ (... )) ซึ่งหมายความว่าการร้องขอไปยังวัตถุจำลองในรหัสทดสอบจะต้องมีการเรียก API จำลองด้วย นอกจากนี้สิ่งนี้มักจะนำไปสู่การทำซ้ำเมื่อ (... ) และตรวจสอบ (เยาะเย้ย) ... การโทร
- ด้วย JMockit ไม่มีการเรียกที่คล้ายกัน แน่นอนว่าเรามีการเรียกตัวสร้าง NonStrictExpectations () และ Verifications () ใหม่ แต่จะเกิดขึ้นเพียงครั้งเดียวต่อการทดสอบ (โดยทั่วไป) และแยกออกจากการเรียกใช้วิธีการและตัวสร้างที่เยาะเย้ย
- Mockito API มีความไม่สอดคล้องกันหลายประการในไวยากรณ์ที่ใช้สำหรับการเรียกใช้วิธีการจำลอง ในเฟสการบันทึกเรามีการเรียกเช่น when (mock.mockedMethod (args)) ... ในขณะที่ในขั้นตอนการตรวจสอบการเรียกเดียวกันนี้จะถูกเขียนเป็นตรวจสอบ (จำลอง) .mockedMethod (args) สังเกตว่าในกรณีแรกการเรียกใช้ MockedMethod จะทำโดยตรงบนวัตถุจำลองในขณะที่ในกรณีที่สองจะทำบนวัตถุที่ส่งคืนโดยการตรวจสอบ (จำลอง)
- JMockit ไม่มีความไม่ลงรอยกันเช่นนี้เนื่องจากการเรียกใช้วิธีการเยาะเย้ยจะทำโดยตรงกับอินสแตนซ์ที่ถูกจำลองโดยตรง (ยกเว้นเพียงข้อเดียวเท่านั้น: ในการจับคู่การเรียกใช้ในอินสแตนซ์จำลองเดียวกันจะใช้การเรียก onInstance (จำลอง) ส่งผลให้รหัสเช่น onInstance (จำลอง) .mockedMethod (args) แม้ว่าการทดสอบส่วนใหญ่จะไม่จำเป็นต้องใช้สิ่งนี้ )
- เช่นเดียวกับเครื่องมือจำลองอื่น ๆ ที่อาศัยวิธีการผูกมัด / การห่อ Mockito ยังใช้ไวยากรณ์ที่ไม่สอดคล้องกันเมื่อทำให้วิธีการโมฆะ ตัวอย่างเช่นคุณเขียนเมื่อ (mockedList.get (1)). thenThrow (new RuntimeException ()); สำหรับวิธีการที่ไม่ใช่โมฆะและ doThrow (RuntimeException ใหม่ ()) เมื่อ (mockedList) .clear (); สำหรับโมฆะ ด้วย JMockit มันจะเป็นไวยากรณ์เดียวกันเสมอ: mockedList.clear (); ผลลัพธ์ = RuntimeException ใหม่ () ;.
- ความไม่ลงรอยกันอีกประการหนึ่งเกิดขึ้นในการใช้สายลับ Mockito: "ล้อเลียน" ที่อนุญาตให้ใช้วิธีการจริงบนอินสแตนซ์ที่สอดแนม ตัวอย่างเช่นหากสปายอ้างถึงรายการที่ว่างเปล่าแทนที่จะเขียนเมื่อ (spy.get (0)) แล้วรีเทิร์น ("foo") คุณจะต้องเขียน doReturn ("foo") เมื่อ (สปาย) .get ( 0) ด้วย JMockit คุณลักษณะการเยาะเย้ยแบบไดนามิกจะให้ฟังก์ชันการทำงานที่คล้ายคลึงกับสายลับ แต่ไม่มีปัญหานี้เนื่องจากวิธีการจริงจะดำเนินการในช่วงการเล่นซ้ำเท่านั้น
- ใน EasyMock และ jMock ซึ่งเป็น API จำลองแรกสำหรับ Java โฟกัสทั้งหมดอยู่ที่การบันทึกการเรียกใช้วิธีการจำลองที่คาดไว้สำหรับอ็อบเจ็กต์จำลองที่ (โดยค่าเริ่มต้น) ไม่อนุญาตให้มีการเรียกใช้ที่ไม่คาดคิด API เหล่านี้ยังมีการบันทึกการเรียกใช้ที่อนุญาตสำหรับอ็อบเจ็กต์จำลองที่อนุญาตให้มีการเรียกใช้ที่ไม่คาดคิด แต่จะถือว่าเป็นคุณลักษณะชั้นสอง นอกจากนี้ด้วยเครื่องมือเหล่านี้ไม่มีวิธีใดในการยืนยันการเรียกร้องให้ล้อเลียนอย่างชัดเจนหลังจากใช้รหัสที่อยู่ระหว่างการทดสอบ การตรวจสอบดังกล่าวทั้งหมดจะดำเนินการโดยปริยายและโดยอัตโนมัติ
- ใน Mockito (และใน Unitils Mock) จะใช้มุมมองที่ตรงกันข้าม การเรียกร้องให้จำลองวัตถุทั้งหมดที่อาจเกิดขึ้นระหว่างการทดสอบไม่ว่าจะบันทึกหรือไม่ก็ตามไม่เคยคาดคิดมาก่อน การตรวจสอบจะดำเนินการอย่างชัดเจนหลังจากใช้รหัสภายใต้การทดสอบแล้วจะไม่ดำเนินการโดยอัตโนมัติ
- ทั้งสองแนวทางนั้นรุนแรงเกินไปและส่งผลให้น้อยกว่าที่เหมาะสม JMockit Expectations & Verifications เป็น API เดียวที่ช่วยให้นักพัฒนาสามารถเลือกชุดค่าผสมที่ดีที่สุดของการเรียกใช้แบบเข้มงวด (โดยค่าเริ่มต้น) และไม่เข้มงวด (อนุญาตโดยค่าเริ่มต้น) สำหรับการทดสอบแต่ละครั้ง
- เพื่อให้ชัดเจนยิ่งขึ้น Mockito API มีข้อบกพร่องดังต่อไปนี้ หากคุณต้องการตรวจสอบว่ามีการเรียกใช้วิธีการจำลองที่ไม่เป็นโมฆะเกิดขึ้นในระหว่างการทดสอบ แต่การทดสอบต้องการค่าส่งคืนจากวิธีการนั้นซึ่งแตกต่างจากค่าเริ่มต้นสำหรับประเภทการส่งคืนการทดสอบ Mockito จะมีรหัสซ้ำ: เมื่อ (mock.someMethod ()) แล้วเรียกคืน (xyz) ในเฟสเรกคอร์ดและการตรวจสอบ (จำลอง) .someMethod () ในขั้นตอนการตรวจสอบ ด้วย JMockit สามารถบันทึกความคาดหวังที่เข้มงวดได้ตลอดเวลาซึ่งไม่จำเป็นต้องได้รับการยืนยันอย่างชัดเจน หรืออีกวิธีหนึ่งสามารถระบุข้อ จำกัด การนับจำนวนคำขอ (ครั้ง = 1) สำหรับความคาดหวังที่ไม่เข้มงวดที่บันทึกไว้ (ด้วยข้อ จำกัด ดังกล่าวของ Mockito สามารถระบุได้ในการเรียกตรวจสอบ (จำลองข้อ จำกัด ) เท่านั้น)
- Mockito มีไวยากรณ์ที่ไม่ดีสำหรับการตรวจสอบตามลำดับและสำหรับการยืนยันแบบเต็ม (นั่นคือการตรวจสอบว่าการเรียกร้องไปยังวัตถุจำลองทั้งหมดได้รับการยืนยันอย่างชัดเจน) ในกรณีแรกจำเป็นต้องสร้างออบเจ็กต์พิเศษและเรียกตรวจสอบสิ่งนั้น: InOrder inOrder = inOrder (mock1, mock2, ... ) ในกรณีที่สองจำเป็นต้องทำการเรียกเช่น VerifyNoMoreInteractions (จำลอง) หรือ VerifyZeroInteractions (mock1, mock2)
- ด้วย JMockit คุณเพียงแค่เขียน VerificationsInOrder () ใหม่หรือ FullVerifications () ใหม่แทน Verifications ใหม่ () (หรือ FullVerificationsInOrder () ใหม่เพื่อรวมข้อกำหนดทั้งสองอย่างเข้าด้วยกัน) ไม่จำเป็นต้องระบุว่าเกี่ยวข้องกับวัตถุจำลองใด ไม่มีการเรียก API จำลองเพิ่มเติม และเป็นโบนัสด้วยการเรียก unverifiedInvocations () ภายในบล็อกการตรวจสอบที่สั่งซื้อคุณสามารถดำเนินการตรวจสอบคำสั่งซื้อที่เป็นไปไม่ได้ใน Mockito
ในที่สุด JMockit Testing Toolkit มีขอบเขตที่กว้างขึ้นและมีเป้าหมายที่ทะเยอทะยานมากกว่าชุดเครื่องมือจำลองอื่น ๆ เพื่อมอบโซลูชันการทดสอบสำหรับนักพัฒนาที่สมบูรณ์และซับซ้อน API ที่ดีสำหรับการล้อเลียนแม้ว่าจะไม่มีข้อ จำกัด เทียม แต่ก็ไม่เพียงพอสำหรับการสร้างการทดสอบที่มีประสิทธิผล เครื่องมือ IDE-agnostic ใช้งานง่ายและบูรณาการอย่างดี Code Coverage ก็เป็นสิ่งสำคัญเช่นกันและนั่นคือสิ่งที่ JMockit Coverage มุ่งหวังที่จะมอบให้ ชุดเครื่องมือทดสอบสำหรับนักพัฒนาอีกชิ้นหนึ่งซึ่งจะมีประโยชน์มากขึ้นเมื่อชุดทดสอบมีขนาดใหญ่ขึ้นคือความสามารถในการเรียกใช้การทดสอบซ้ำเพิ่มขึ้นหลังจากการเปลี่ยนรหัสการผลิตเป็นภาษาท้องถิ่น ซึ่งรวมอยู่ในเครื่องมือการครอบคลุม
(ได้รับแหล่งที่มาอาจมีความลำเอียง แต่ก็ ... )
ผมว่าไปกับJMockit เป็นวิธีที่ง่ายที่สุดในการใช้งานยืดหยุ่นและใช้ได้กับทุกกรณีแม้แต่ในกรณีและสถานการณ์ที่ยากลำบากเมื่อคุณไม่สามารถควบคุมชั้นเรียนที่จะทดสอบได้ (หรือคุณไม่สามารถทำลายได้เนื่องจากเหตุผลด้านความเข้ากันได้เป็นต้น)
ประสบการณ์ของฉันกับ JMockit เป็นไปในเชิงบวกมาก