mocks ละเมิดหลักการเปิด / ปิดหรือไม่?


13

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

สิ่งนี้เรียกร้องความสนใจของฉันเมื่อเราพูดถึงการล้อเลียน เนื่องจากการเยาะเย้ยต้องอาศัยการเรียกร้องอย่างคาดหวังจากระบบภายใต้การทดสอบของผู้ทดสอบดังนั้น mocks จึงผนวกเข้ากับการใช้งานอย่างแน่นหนามากกว่าอินเตอร์เฟส

ในขณะที่ทำการค้นคว้าmock vs stubหลายบทความเห็นด้วยว่าควรใช้ต้นขั้วแทน mocks เนื่องจากไม่ต้องพึ่งพาความคาดหวังจากการพึ่งพาซึ่งหมายความว่าการทดสอบไม่จำเป็นต้องมีความรู้เกี่ยวกับระบบพื้นฐานภายใต้การใช้งานการทดสอบ

คำถามของฉันจะเป็น:

  1. mocks ละเมิดหลักการเปิด / ปิดหรือไม่?
  2. มีบางสิ่งที่ขาดหายไปในการโต้แย้งเพื่อช่วยสตับในย่อหน้าสุดท้ายที่ทำให้สตับไม่ดีเทียบกับ mocks หรือไม่?
  3. ถ้าเป็นเช่นนั้นเมื่อไรจะเป็นกรณีการใช้งานที่ดีในการเยาะเย้ยและเมื่อไหร่จะเป็นกรณีการใช้งานที่ดีในการใช้สตับ


8
Since mocking relays heavily on expectation calls from system under test's dependencies...ฉันคิดว่านี่เป็นที่ที่คุณจะเบี้ยว การเยาะเย้ยเป็นตัวแทนบางส่วนของระบบภายนอก มันไม่ได้เป็นตัวแทนของระบบภายนอกในทางใดทางหนึ่งยกเว้นตราบเท่าที่มันจำลองระบบภายนอกในลักษณะที่ช่วยให้การทดสอบจะทำงานกับรหัสที่มีการพึ่งพาระบบภายนอกดังกล่าว คุณยังคงต้องมีการทดสอบการรวมระบบเพื่อพิสูจน์ว่ารหัสของคุณทำงานได้กับระบบจริงที่ไม่ได้ทำการติดตั้ง
Robert Harvey

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

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

คำตอบ:


4
  1. ฉันไม่เห็นว่าทำไม mocks จะละเมิดหลักการเปิด / ปิด หากคุณสามารถอธิบายให้เราทราบได้ว่าเหตุใดคุณจึงคิดว่าพวกเขาอาจทำเราอาจสามารถบรรเทาข้อกังวลของคุณได้

  2. ข้อเสียเปรียบเพียงอย่างเดียวของสตับที่ฉันคิดได้ก็คือโดยทั่วไปแล้วพวกเขาต้องการงานเขียนมากกว่า mocks เนื่องจากแต่ละอันนั้นเป็นทางเลือกในการใช้อินเตอร์เฟสที่ต้องพึ่งพาดังนั้นโดยทั่วไปแล้วจะต้องทำให้เสร็จสมบูรณ์ การดำเนินการตามส่วนต่อประสาน เพื่อให้เป็นตัวอย่างที่ดีที่สุดหากระบบย่อยของคุณภายใต้การทดสอบเรียกใช้ RDBMS การจำลองของ RDBMS จะตอบสนองต่อการสอบถามเฉพาะที่ทราบว่าระบบย่อยที่อยู่ภายใต้การทดสอบนั้นยอมให้ชุดข้อมูลทดสอบที่กำหนดไว้ล่วงหน้า ในทางกลับกันการนำไปใช้ทางเลือกอื่นจะเป็น RDBMS ในหน่วยความจำเต็มรูปแบบซึ่งอาจเป็นภาระพิเศษที่ต้องจำลองพฤติกรรมของ RDBMS ไคลเอนต์ - เซิร์ฟเวอร์จริงที่คุณใช้ในการผลิต (โชคดีที่เรามีสิ่งต่าง ๆ เช่น HSQLDB ดังนั้นเราจึงสามารถทำเช่นนั้นได้ แต่ก็ยัง

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

PS บุคคลที่คุณจะหมายถึงจะได้รับฉันในหนึ่งในคำตอบการทดสอบที่เกี่ยวข้องอื่น ๆ ของฉันที่นี่ใน programmers.stackexchange.com เช่นนี้


an alternative implementation would be a full-blown in-memory RDBMS- คุณไม่จำเป็นต้องไปไกลขนาดนั้นด้วยต้นขั้ว
Robert Harvey

@RobertHarvey ดีกับ HSQLDB และ H2 มันไม่ยากที่จะไปได้ไกลขนาดนั้น มันอาจเป็นเรื่องยากกว่าที่จะทำบางสิ่งบางอย่างเพื่อไม่ให้ไปไกลขนาดนั้น แต่ถ้าคุณตัดสินใจที่จะทำด้วยตัวเองคุณจะต้องเริ่มต้นด้วยการเขียนตัวแยกวิเคราะห์ SQL แน่นอนว่าคุณสามารถตัดมุมบาง แต่มีการทำงานมาก อย่างไรก็ตามอย่างที่ฉันได้กล่าวไว้ข้างต้นนี่เป็นเพียงตัวอย่างสุดขั้ว
Mike Nakis

9
  1. หลักการเปิด / ปิดส่วนใหญ่เกี่ยวกับความสามารถในการเปลี่ยนพฤติกรรมของชั้นเรียนโดยไม่ต้องแก้ไขมัน ดังนั้นการฉีดการพึ่งพาองค์ประกอบที่เยาะเย้ยในชั้นเรียนภายใต้การทดสอบไม่ได้ละเมิด

  2. ปัญหาของการทดสอบเป็นสองเท่า (จำลอง / ต้นขั้ว) ก็คือโดยพื้นฐานแล้วคุณจะตั้งสมมุติฐานโดยพลการเกี่ยวกับวิธีที่คลาสภายใต้การทดสอบโต้ตอบกับสภาพแวดล้อมของมัน หากความคาดหวังเหล่านั้นผิดคุณอาจมีปัญหาบางอย่างเมื่อมีการใช้รหัส หากคุณสามารถซื้อได้ให้ทดสอบโค้ดของคุณภายในข้อ จำกัด เดียวกันกับสภาพแวดล้อมการผลิตของคุณ หากคุณไม่สามารถทำได้ให้ตั้งสมมติฐานน้อยที่สุดและจำลองอุปกรณ์ต่อพ่วงของระบบของคุณเท่านั้น (ฐานข้อมูลบริการตรวจสอบความถูกต้องไคลเอนต์ HTTP ฯลฯ ... )

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


6

หมายเหตุ: ฉันสมมติว่าคุณกำหนด Mock ให้หมายถึง "คลาสที่ไม่มีการใช้งานเพียงบางสิ่งที่คุณสามารถตรวจสอบได้" และ Stub ให้เป็น "จำลองบางส่วนหรือที่เรียกว่าพฤติกรรมบางอย่างของคลาสที่นำมาใช้" ตามสแต็กนี้ คำถามที่มากเกิน

ฉันไม่แน่ใจว่าทำไมคุณคิดว่าฉันทามติที่จะใช้สตับเช่นมันเป็นสิ่งที่ตรงกันข้ามในMockito Documentation

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

อย่างไรก็ตามมีบางกรณีที่ mocks บางส่วนเกิดประโยชน์: การจัดการกับโค้ดที่คุณไม่สามารถเปลี่ยนแปลงได้อย่างง่ายดาย (อินเทอร์เฟซของบุคคลที่ 3 การเปลี่ยนรหัสดั้งเดิมแบบชั่วคราว ฯลฯ ) อย่างไรก็ตามฉันจะไม่ใช้ mocks บางส่วนสำหรับการทดสอบใหม่ รหัสที่ออกแบบ

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


2
จากคำถาม SO ที่ให้มาเหมือนกัน (คำตอบที่ยอมรับ) นี่คือคำจำกัดความของ Mock / Stub ที่ฉันอ้างถึงเช่นกัน: วัตถุจำลองนั้นใช้เพื่อกำหนดความคาดหวังเช่น: ในสถานการณ์สมมตินี้ฉันคาดว่าจะเรียกเมธอด A () ด้วยพารามิเตอร์ดังกล่าว Mocks บันทึกและตรวจสอบความคาดหวังดังกล่าว Stubs มีวัตถุประสงค์ที่แตกต่างกัน: พวกเขาไม่ได้บันทึกหรือตรวจสอบความคาดหวัง แต่อนุญาตให้เรา "แทนที่" พฤติกรรมสถานะของวัตถุ "ปลอม" เพื่อใช้ในสถานการณ์ทดสอบ ...
Christopher Francisco

2

ฉันคิดว่าปัญหาอาจเกิดขึ้นจากการสันนิษฐานว่าการทดสอบที่ถูกต้องเพียงอย่างเดียวคือการทดสอบแบบเปิด / ปิด

มันง่ายที่จะเห็นว่าการทดสอบเพียงอย่างเดียวที่ควรมีความสำคัญคือสิ่งที่ทดสอบส่วนต่อประสาน อย่างไรก็ตามในความเป็นจริงมันมักจะมีประสิทธิภาพมากกว่าในการทดสอบส่วนต่อประสานนั้นโดยการทดสอบการทำงานภายใน

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

Tl / Dr: การทำ "โดยหนังสือ" เป็นเรื่องที่ดี แต่เมื่อการผลักมาถึงการมีผลิตภัณฑ์บนโต๊ะทำงานของเจ้านายของคุณในวันศุกร์จะมีประโยชน์มากกว่าชุดทดสอบโดยหนังสือซึ่งจะใช้เวลาจนความร้อน จักรวาลเพื่อยืนยันความสอดคล้อง

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