การเยาะเย้ยระดับคอนกรีต - ไม่แนะนำ


11

ฉันเพิ่งอ่านข้อความที่ตัดตอนมาจากหนังสือ "Growing Object-Oriented Software" ซึ่งอธิบายเหตุผลบางอย่างว่าทำไมไม่แนะนำให้ใช้การล้อเลียนระดับคอนกรีต

นี่คือตัวอย่างโค้ดของการทดสอบหน่วยสำหรับคลาส MusicCentre:

public class MusicCentreTest {
  @Test public void startsCdPlayerAtTimeRequested() {
    final MutableTime scheduledTime = new MutableTime();
    CdPlayer player = new CdPlayer() { 
      @Override 
      public void scheduleToStartAt(Time startTime) {
        scheduledTime.set(startTime);
      }
    }

    MusicCentre centre = new MusicCentre(player);
    centre.startMediaAt(LATER);

    assertEquals(LATER, scheduledTime.get());
  }
}

และคำอธิบายแรกของเขา:

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

ฉันไม่สามารถเข้าใจสิ่งที่เขาหมายถึงเมื่อเขาพูดว่า:

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

ผมเข้าใจว่าสอดคล้องกับการบริการให้กับMusicCentreวิธีการ startMediaAt'เรียกว่า

เขาหมายถึงอะไร "ที่อื่น"?

ข้อความที่ตัดตอนมาที่สมบูรณ์อยู่ที่นี่: http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html


เพิ่มความคิดเห็นในบล็อกของเขาเนื่องจากฉันไม่สามารถเข้าใจความหมายของคำพูดเหล่านี้ได้
oligofren

@oligofren มันเป็นปริศนาที่ยอดเยี่ยมจริงๆ :) ...
Mik378

คำตอบ:


6

ผู้เขียนของโพสต์นั้นส่งเสริมการใช้อินเทอร์เฟซมากกว่าการใช้คลาสสมาชิก

It turns out that my MusicCentre object only uses the starting and stopping methods on the CdPlayer, the rest are used by some other part of the system. I'm over-specifying my MediaCentre by requiring it to talk to a CdPlayer, what it actually needs is a ScheduledDevice.

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

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

การอ้างสิทธิ์จะเริ่มมีเหตุผลมากขึ้นเมื่อคุณระเบิดการทำงานที่เป็นไปได้ทั้งหมด:

  • เริ่มต้น
  • หยุด
  • หยุด
  • บันทึก
  • ลำดับการเล่นแบบสุ่ม
  • แทร็กตัวอย่างจุดเริ่มต้นของเพลง
  • เพลงตัวอย่างสุ่มเพลง
  • ให้ข้อมูลสื่อ
  • ...

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

ในที่สุดหลักฐานของผู้เขียนคือการใช้เฉพาะสิ่งที่จำเป็นและทำให้ชัดเจนต่อผู้ดูแลในอนาคตสิ่งที่จำเป็นก่อน เป้าหมายคือการลดการทำใหม่ / วิเคราะห์โมดูลของรหัสใหม่ในระหว่างการบำรุงรักษาที่ตามมา


ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมนี้ อย่างไรก็ตามคุณกล่าวว่า: "การรันการทดสอบหน่วยในฟังก์ชั่นอื่น ๆ ทั้งหมดเป็นความพยายามในการทดสอบที่ไม่ได้ใช้เพราะมันอยู่ในสถานะ" ไม่สนใจ " มันไม่ได้ค่อนข้าง: "การสร้าง mocks สำหรับแต่ละฟังก์ชั่นอื่น ๆ เป็นความพยายามในการทดสอบเพราะพวกเขาอยู่ในสถานะ" ไม่สนใจ "
Mik378

@ Mik378 - ใช่นั่นคือสิ่งที่ฉันได้รับฉันเพียงแค่ใช้ถ้อยคำที่แตกต่างออกไป และฉันได้ปรับปรุงคำตอบเพื่อให้ชัดเจนยิ่งขึ้น

แต่ฉันพบว่าคำว่า "การทดสอบหน่วยใช้งาน" นั้นทำให้สับสน นั่นหมายความว่า MusicCentre กำลังจะทำการทดสอบผู้ทำงานร่วมกันในขณะที่ในความเป็นจริง MOCKS จะทำงานร่วมกันเพื่อทำการทดสอบบริการของตัวเอง โดยวิธีการที่ตอนนี้ผมเข้าใจความหมาย :)
Mik378

@ Mik378 - เรากำลังพูดในสิ่งเดียวกันและฉันอาจใช้คำศัพท์ที่น้อยกว่าเพื่อที่จะทำเช่นนั้น ขออภัยในความสับสน

4

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

หลังจากคิดมากเกี่ยวกับเรื่องนี้ฉันได้รับการตีความที่เป็นไปได้ของคำพูดนี้:

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

ในตัวอย่างข้างต้น, CDPlayerช่วงเวลาที่จะแสดงโดยวัตถุทั้งเต็มรูปแบบชื่อ ดังนั้นจึงยังคงนำไปสู่ความสัมพันธ์โดยนัยระหว่างMusicCentreและ "ความจริงของการกำหนดเวลา"

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


ฉันคิดว่าคุณได้รับแล้ว น่าเสียดายที่ฉันไม่ได้รับการแจ้งเตือนความคิดเห็นของคุณ
Steve Freeman

3

บริการที่ฉันหมายถึงที่นี่คือ CDPlayer.scheduleToStartAt () นั่นคือสิ่งที่ MediaCentre เรียก - ผู้ทำงานร่วมกันที่ต้องการใช้งาน MediaCentre เป็นวัตถุที่อยู่ระหว่างการทดสอบ

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

มันช่วยได้ไหม


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