วัตถุจำลองใช้ผิดวิธีอย่างไร


15

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


เป็นบทความที่คุณอ่านบทความนี้หรือไม่ martinfowler.com/articles/mocksArentStubs.html
keppla

ไม่ ... ไม่สามารถจำแหล่งที่มาที่แน่นอน แต่จะโพสต์ที่นี่ถ้าฉันทำ
อาร์มันด์

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

คำตอบ:


13

ฉันเกลียดที่จะเห็นชั้นเรียนคอนกรีตที่เรียบง่ายล้อเลียนไป ตัวอย่างเช่นใช้คลาสแบบง่าย ๆ ดังต่อไปนี้ซึ่งไม่มีการพึ่งพาสิ่งอื่นใด:

public class Person
{
    private readonly string _firstName;
    private readonly string _surname;

    public Person(string firstName, string surname)
    {
        if (String.IsNullOrEmpty(firstName))
        {
            throw new ArgumentException("Must have first name");
        }

        if (String.IsNullOrEmpty(surname))
        {
            throw new ArgumentException("Must have a surname");
        }

        _firstName = firstName;
        _surname = surname;
    }

    public string Name 
    {
        get
        {
            return _firstName + " " + _surname;
        }
    }
}

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

ในฐานะที่เป็นส่วนขยายของสิ่งนี้ฉันได้เห็นผู้คนเขียนการทดสอบที่จำลองถูกตั้งค่าด้วยความคาดหวังจากนั้นจำลองนั้นจะถูกเรียกโดยตรงในการทดสอบ การทดสอบที่น่าประหลาดใจจะผ่าน ... hmmmm ...


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

5
ไม่เปลี่ยนปัญหา - สิ่งที่เรียบง่ายแบบนี้โดยทั่วไปไม่ควรล้อเลียนแม้ว่าคุณจะใช้บางอย่างที่ผ่อนคลายข้อ จำกัด ทางเทคนิคกับสิ่งที่สามารถล้อเลียนได้ (เช่นกรอบจำลองเช่น TypeMock หรือภาษาไดนามิก)
FinnNk

กฎง่ายๆของฉันคือการเยาะเย้ยพฤติกรรมเสมอไม่ใช่เพื่อข้อมูล
ardave

10

มันอาจฟังดูชัดเจน แต่: อย่าใช้วัตถุจำลองในรหัสการผลิต! ฉันเคยเห็นตัวอย่างมากกว่าหนึ่งตัวอย่างซึ่งรหัสการผลิตขึ้นอยู่กับลักษณะของวัตถุจำลองบางอย่าง ( MockHttpServletRequestจากตัวอย่างของ Springframework)


14
ฉันหวังว่าคุณจะทำตามหน้าที่ศักดิ์สิทธิ์ของคุณและส่งรหัสไปที่ DailyWTF?
keppla

1
ในงานก่อนหน้าของเราเราถูกห้ามอย่างชัดแจ้งจากการส่งอะไรจาก codebase ของเราไปยัง DWTF
quant_dev

9
@quant_dev: ความจริงที่ว่าพวกเขามีนโยบายดังกล่าวหมายถึงสิ่งที่น่ากลัวเกี่ยวกับการพัฒนาของพวกเขา ...
จอห์นฟิชเชอร์

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

เพียงแค่ใช้ทางลัดไม่ได้จริงๆพอที่จะรับคุณ dailywtf ...
poolie

9

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

วิธีแก้ไขปัญหานี้เริ่มใช้สตับแทน mocks บทความที่ฉันพบว่าให้ความกระจ่างเกี่ยวกับเรื่องนี้เป็นเรื่องที่พบใน Javadoc ของ Mockito: http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html (ดู "2. การขัดถูบางอย่าง?" ) เชื่อมโยงไปยัง: http://monkeyisland.pl/2008/07/12/should-i-worry-about-the-unexpected/

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

มีหนังสืออยู่สองสามเล่มที่นี่และฉันขอแนะนำให้แตะที่เรื่องนี้และเยาะเย้ยและเรื่องทั่วไป:

รูปแบบ xUnit

ศิลปะแห่งการทดสอบหน่วย: ด้วยตัวอย่างใน. Net

การทดสอบ Java ยุคใหม่: TestNG และแนวคิดขั้นสูง (หนังสือเล่มนี้ส่วนใหญ่เกี่ยวกับ testNG แต่มีบทที่ดีเกี่ยวกับการเยาะเย้ย)


+1 สำหรับประเด็นการตรวจสอบการเรียกใช้เมธอดมากเกินไป อย่างไรก็ตามมักจะมีด้านพลิกของเหรียญที่มีการเรียกวิธีการที่ไม่คาดคิดทำให้เกิดความล้มเหลวในวิธีการของคุณ โชคดีที่ Mockito มีการAnswer.RETURNS_SMART_NULLSตั้งค่าสำหรับ mocks ซึ่งช่วยวินิจฉัยปัญหานี้
Bringer128

4

ฉันสังเกตรูปแบบการต่อต้านเพียงเล็กน้อยในประสบการณ์ของฉัน

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

มิฉะนั้นประสบการณ์ของฉันกับ mocks โดยเฉพาะ Mockito เป็นเรื่องง่าย พวกเขาทำแบบทดสอบง่ายมากในการเขียนและบำรุงรักษา การทดสอบการโต้ตอบ / การเสนอมุมมองของ GWT นั้นทำได้ง่ายกว่าด้วย mocks มากกว่า GWTTestCase


2 และ 3 เป็นปัญหาที่แน่นอน! คุณมีตัวอย่างง่ายๆของ (1) หรือไม่?
อาร์มันด์

2

ฉันพบว่าการทดสอบที่ใช้ mocks ในหลาย ๆ ชั้นของแอปพลิเคชันโดยเฉพาะอย่างยิ่งยากที่จะถอดรหัสและเปลี่ยนแปลง อย่างไรก็ตามฉันคิดว่าสิ่งนี้ได้รับการลดลงในช่วงไม่กี่ปีที่ผ่านมาด้วยการปรับปรุง API เฟรมเวิร์กของจำลอง

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

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

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