ทำไม Mockito ไม่จำลองวิธีการแบบคงที่?


267

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

ฉันรู้กรอบการเยาะเย้ยอื่น ๆ เช่น PowerMock สามารถทำได้ แต่ทำไม Mockito ถึงไม่สามารถทำได้?

ฉันอ่านบทความนี้แต่ผู้เขียนดูเหมือนจะต่อต้านคำอย่างเคร่งศาสนาstaticบางทีมันอาจเป็นความเข้าใจที่ไม่ดีของฉัน

คำอธิบาย / ลิงค์ง่าย ๆ น่าจะดีมาก


12
เพียงหมายเหตุด้านข้าง: PowerMock ไม่ใช่ไลบรารี่จำลองวัตถุต่อไฟล์เพียงแค่เพิ่มฟีเจอร์เหล่านั้น (การเยาะเย้ยสถิติและ cker) ที่ด้านบนของไลบรารีอื่น ๆ เราใช้ PowerMock + Mockito ในที่ทำงานพวกเขาลอยได้ดีซึ่งกันและกัน
Matthias

คำตอบ:


238

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

วิธีเดียวที่จะเลียนแบบสถิตศาสตร์คือการแก้ไขรหัสไบต์ของคลาสเมื่อรันไทม์ซึ่งฉันคิดว่ามีส่วนเกี่ยวข้องมากกว่าการสืบทอดเล็กน้อย

นั่นคือสิ่งที่ฉันคาดเดาสำหรับสิ่งที่คุ้มค่า ...


7
เช่นเดียวกันกับการเยาะเย้ยคอนสตรัคเตอร์ สิ่งเหล่านั้นก็ไม่สามารถเปลี่ยนแปลงได้ด้วยการสืบทอด
Matthias

11
มันอาจจะคุ้มที่จะเพิ่มว่าผู้เสนอ TDD / TBD บางคนรับรู้ถึงการขาดวิธีการคงที่และการเยาะเย้ยคอนสตรัคเตอร์เป็นสิ่งที่ดี พวกเขาให้เหตุผลว่าเมื่อคุณพบว่าตัวเองต้องเยาะเย้ยวิธีหรือตัวสร้างแบบคงที่นี่เป็นตัวบ่งชี้สำหรับการออกแบบระดับที่ไม่ดี ตัวอย่างเช่นเมื่อทำตามแนวทาง IoC แบบพิถีพิถันในการประกอบโมดูลรหัสของคุณคุณจะไม่จำเป็นต้องล้อเลียนสถิติหรือ cker ในตอนแรก (เว้นแต่พวกเขาจะเป็นส่วนหนึ่งขององค์ประกอบกล่องดำของหลักสูตร) ดูเพิ่มเติมที่giorgiosironi.blogspot.com/2009/11/…
Matthias

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

11
@ Lo-Tan - เหมือนกับการพูดว่าภาษาควรมีความสามารถในทุกสิ่งโดยไม่คิดว่ามันจะดีกว่าคุณ นั่นเป็นเพียงความว่างเปล่าในส่วนของคุณเพราะพวกเขาออกมาอย่างสง่างาม ปัญหาที่นี่คือการต่อสู้ "ต่อต้าน / โปรคงที่" ไม่ชัดเจนและเป็นกรอบ ฉันยอมรับว่าเราควรมีทั้งคู่ แต่ที่ข้อเท็จจริงเป็นที่ชัดเจนผมชอบกรอบการทำงานที่มีการเรียกเก็บข้อเท็จจริงเหล่านั้น นั่นเป็นวิธีหนึ่งในการเรียนรู้ - เครื่องมือที่ช่วยให้คุณติดตาม ดังนั้นคุณเองไม่จำเป็นต้อง แต่ตอนนี้ทุกหัวก๋วยเตี๋ยวสามารถกำหนดสิ่งที่เรียกว่า "การออกแบบที่ดี" ของพวกเขา "ข้อบกพร่องพื้นฐาน" ...
nevvermind

13
@nevvermind ใช่มั้ย ภาษาระดับสูงมีไว้เพื่อช่วยให้คุณและมี abstractions ที่จำเป็นเพื่อให้คุณสามารถมุ่งเน้นไปที่ชิ้นสำคัญ ห้องสมุดทดสอบเป็นเครื่องมือ - เครื่องมือที่ฉันใช้ในการสร้างคุณภาพที่ดีขึ้นและหวังว่าจะออกแบบโค้ดให้ดีขึ้น อะไรคือประเด็นของห้องสมุดทดสอบ / จำลองเมื่อมีข้อ จำกัด ซึ่งอาจหมายความว่าฉันไม่สามารถใช้งานได้เมื่อฉันต้องรวมรหัสที่ออกแบบมาไม่ดีของคนอื่น ดูเหมือนจะไม่คิดว่าดีออกในขณะที่ภาษาที่ดีได้รับ
Lo-Tan

28

หากคุณต้องการเยาะเย้ยวิธีการคงที่ก็เป็นตัวบ่งชี้ที่แข็งแกร่งสำหรับการออกแบบที่ไม่ดี โดยปกติแล้วคุณจะล้อเลียนการพึ่งพาการทดสอบในห้องเรียนของคุณ หาก class-under-test ของคุณอ้างถึงวิธีการคงที่ - เช่น java.util.Math # sin เช่น - นั่นหมายถึง class-under-test ต้องการการติดตั้งที่แน่นอน (เช่นความแม่นยำและความเร็ว) หากคุณต้องการสรุปจากการใช้ไซนัสอย่างเป็นรูปธรรมคุณอาจต้องใช้ส่วนต่อประสาน (คุณจะเห็นว่าจะเกิดอะไรขึ้น)


3
ฉันใช้วิธีการคงที่เพื่อสร้าง abstractions ระดับสูงเช่น "ส่วนต่อเนื่องคงที่" ส่วนหน้าดังกล่าวจะช่วยป้องกันรหัสลูกค้าจากความซับซ้อนและรายละเอียดในระดับต่ำของ ORM API ซึ่งให้ API ที่สอดคล้องและใช้งานได้ง่ายขึ้นในขณะที่ให้ความยืดหยุ่นมากมาย
Rogério

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

88
จริง แต่บางครั้งคุณอาจไม่มีทางเลือกถ้าเช่นคุณต้องจำลองวิธีการคงที่ซึ่งอยู่ในระดับบุคคลที่สามบางส่วน
Stijn Geukens

6
จริง แต่บางครั้งเราอาจต้องจัดการกับซิงเกิล
มนู Manjunath

สิ่งเดียวที่คิดว่าไม่สามารถแก้ไขได้ด้วยการสรุปเป็นนามธรรมมากเกินไประดับนามธรรม ... การเพิ่มเลเยอร์สิ่งที่เป็นนามธรรมเพิ่มความซับซ้อนและมักไม่จำเป็น ฉันคิดถึงตัวอย่างที่ฉันเห็นของเฟรมเวิร์กที่พยายามเยาะเย้ย System.currentTimeMillis () โดยการตัดการเรียกง่ายๆนี้ไว้ในคลาสเดียว เราลงเอยด้วยคลาสซิงเกิลตันต่อวิธีแทนที่จะเป็นแค่วิธีการ - เพื่อการทดสอบที่ง่ายขึ้นเท่านั้น และเมื่อคุณแนะนำบุคคลที่สามที่เรียกวิธีการคงที่โดยตรงแทนที่จะผ่านเสื้อคลุมเดี่ยวของคุณการทดสอบล้มเหลวแล้ว ...
Fr Jeremy Krieg

5

ฉันคิดว่ามันเป็นกลิ่นรหัสอย่างจริงจังถ้าคุณต้องการวิธีการจำลองแบบคงที่เช่นกัน

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

ครั้งเดียวที่มันดูเหมือนว่าเกินความจริงสำหรับฉันนั่นก็คือ libs อย่าง Guava แต่คุณไม่จำเป็นต้องเยาะเย้ยแบบนี้อีกต่อไปเพราะมันเป็นส่วนหนึ่งของตรรกะ ... (อย่าง Iterables.transform (.. ))
ในแบบของคุณ อยู่ที่สะอาดคุณสามารถเยาะเย้ยการพึ่งพาของคุณทั้งหมดในวิธีที่สะอาดและคุณมีชั้นต่อต้านการทุจริตกับการพึ่งพาภายนอก ฉันได้เห็น PowerMock ในทางปฏิบัติและทุกชั้นเรียนที่เราต้องการสำหรับการออกแบบไม่ดี การรวมกันของ PowerMock ในบางครั้งทำให้เกิดปัญหาร้ายแรง
(เช่นhttps://code.google.com/p/powermock/issues/detail?id=355 )

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


2
Singleton จะทำให้คุณพบกับปัญหาทุกประเภทโดยเฉพาะอย่างยิ่งเมื่อคุณตระหนักว่าคุณต้องการมากกว่าหนึ่งอินสแตนซ์และตอนนี้คุณจำเป็นต้องปรับโครงสร้างระบบทั้งหมดของคุณใหม่เพื่อให้มันเกิดขึ้น
Ricardo Freitas

ฉันไม่ได้บอกว่าฉันแนะนำรูปแบบซิงเกิลให้กับทุกคน สิ่งที่ฉันหมายถึงคือถ้าฉันต้องตัดสินใจระหว่างคลาสยูทิลิตี้สแตติกและ Singleton ที่เสนอฟังก์ชั่นเดียวกันฉันจะเลือกซิงเกิล และถ้าชั้นเป็นซิงเกิลหรือไม่ควรถูกควบคุมโดยกรอบ DI อยู่แล้วในชั้นเรียนของฉันของฉันและในการกำหนดค่าของฉันฉันกำหนด@Inject SomeDependency bind(SomeDependency.class).in(Singleton.class)ดังนั้นถ้าพรุ่งนี้ไม่ใช่ Singleton อีกต่อไปฉันจะเปลี่ยนการตั้งค่าและนั่นคือ
pete83

@ pete83 ฉันได้ยินคุณพี่ชาย อย่างไรก็ตามฉันมีปัญหากับการทดสอบ libs หรือเฟรมเวิร์กที่ต้องการให้ devs เปลี่ยนการออกแบบเพื่อให้เป็นไปตามการออกแบบ / ขีด จำกัด ของกรอบการทดสอบ นั่นคือ IMO ที่วางเกวียนก่อนม้าหรือหางกระดิกสุนัข
แมตต์แคมป์เบล

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

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

4

Mockito ส่งคืนวัตถุ แต่คงที่หมายถึง "ระดับชั้นไม่ใช่ระดับวัตถุ" ดังนั้น mockito จะให้ข้อยกเว้นตัวชี้โมฆะสำหรับคงที่


0

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


1
การเยาะเย้ยของวิธีการแบบคงที่นั้นง่ายกว่าการเยาะเย้ยวิธีการแบบอินสแตนซ์ (เนื่องจากไม่มีกรณี) เมื่อใช้ API การเยาะเย้ยที่เหมาะสม
Rogério

นี่เป็นเหมือนการตอบคำถามด้วยคำถามซึ่งเป็นสาเหตุว่าทำไมจึงยากที่จะทำเช่นนั้นซึ่งไม่ใช่คำตอบ
Matthias

40
ฉันลงเพราะโพสต์บล็อกแนะนำการแก้ปัญหาค่าใช้จ่ายสูง (refactoring รหัสการผลิต) แทนที่จะแก้ปัญหาของการแยกชั้นเรียนจากวิธีการคงที่ที่จะใช้ IMO เครื่องมือที่เยาะเย้ยซึ่งทำหน้าที่อย่างแท้จริงจะไม่แยกแยะกับวิธีการใด ๆ ผู้พัฒนาควรมีอิสระในการตัดสินใจว่าการใช้วิธีการแบบคงที่ดีหรือไม่ดีในสถานการณ์ที่กำหนดแทนที่จะถูกบังคับให้ใช้เส้นทางเดียว
Rogério
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.