การเขียนรหัสที่สามารถทดสอบได้เทียบกับการหลีกเลี่ยงความเอนเอียงแบบเก็งกำไร


11

ฉันอ่านข้อความในบล็อกเมื่อเช้านี้และพบสิ่งนี้ :

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

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

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

ดังนั้นเราจะคืนดีการแลกเปลี่ยนนี้ได้อย่างไร มันคุ้มค่าหรือไม่ที่จะเป็นการเก็งกำไรโดยทั่วไปเพื่ออำนวยความสะดวกในการทดสอบ / TDD? หากคุณใช้การทดสอบเป็นสองเท่าให้นับเป็นการนำไปใช้ครั้งที่สองและทำให้ความคิดทั่วๆไปไม่เป็นการเก็งกำไรอีกต่อไป? คุณควรพิจารณากรอบการเยาะเย้ยเฮฟวี่เวทที่ช่วยให้ผู้เยาะเย้ยเป็นรูปธรรมมากขึ้น (เช่นโมลเทียบกับ Moq ใน C # โลก) หรือไม่ หรือคุณควรทดสอบกับชั้นเรียนที่เป็นรูปธรรมและเขียนสิ่งที่อาจพิจารณาว่าเป็นการทดสอบ "การรวม" จนกว่าจะถึงเวลาที่การออกแบบของคุณต้องมีความหลากหลาย

ฉันอยากรู้อยากเห็นที่จะอ่านคนอื่นจะใช้เวลาในเรื่องนี้ - ขอบคุณล่วงหน้าสำหรับความคิดเห็นของคุณ


โดยส่วนตัวแล้วฉันคิดว่าเอนทิตี้ไม่ควรถูกเยาะเย้ย ฉันล้อเลียนบริการเท่านั้นและสิ่งเหล่านั้นต้องการอินเทอร์เฟซไม่ว่าในกรณีใดเนื่องจากรหัสโดเมนรหัสมักไม่มีการอ้างอิงถึงรหัสที่มีการใช้บริการ
CodesInChaos

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

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

@ WinstonEwert นั่นเป็นประโยชน์ที่น่าสนใจของการพิมพ์แบบไดนามิกที่ฉันไม่เคยพิจารณามาก่อนในฐานะคนที่คุณชี้ให้เห็นว่าโดยทั่วไปแล้วจะไม่ทำงานในภาษาที่พิมพ์แบบไดนามิก
Erik Dietrich

@CodeInChaos ฉันไม่ได้พิจารณาความแตกต่างสำหรับวัตถุประสงค์ของคำถามนี้แม้ว่ามันจะเป็นความแตกต่างที่สมเหตุสมผล ในกรณีนี้เราอาจนึกถึงคลาสบริการ / กรอบที่มีการใช้งานเพียงหนึ่ง (ปัจจุบัน) สมมติว่าฉันมีฐานข้อมูลที่ฉันเข้าถึงด้วย DAO ฉันไม่ควรเชื่อมต่อ DAO จนกว่าฉันจะมีโมเดลการติดตารองอยู่หรือไม่? (นั่นน่าจะเป็นสิ่งที่ผู้เขียนโพสต์บล็อกหมายถึง)
Erik Dietrich

คำตอบ:


14

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


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

1
นี่คือคำตอบที่ฉันพบว่าน่าเชื่อถือที่สุด (และ @KilianFoth เพิ่มว่ามีการใช้งานโค้ดที่สองหรือไม่) ฉันจะระงับการยอมรับคำตอบเล็กน้อยเพื่อดูว่ามีใครบางคนในการตีระฆัง
Erik Dietrich

ฉันยังจะเพิ่มเมื่อคุณขึ้นอยู่กับอินเทอร์เฟซในการทดสอบทั่วไปจะไม่เก็งกำไรอีกต่อไป
Pete

"ไม่ทำเช่นนั้น" (โดยใช้อินเทอร์เฟซ) ไม่ส่งผลให้คลาสของคุณมีการเชื่อมโยงอย่างแน่นหนาโดยอัตโนมัติ มันแค่ทำไม่ได้ เช่นใน. NET Framework มีStreamคลาส แต่ไม่มีข้อต่อคับ
Luke Puplett

3

รหัสทดสอบทั่วไปนั้นไม่ใช่เรื่องง่าย ถ้าเป็นเช่นนั้นเราจะทำมันมานานแล้วและไม่ได้ทำเรื่องนี้สักหน่อยในช่วง 10-15 ปีที่ผ่านมา หนึ่งในปัญหาที่ใหญ่ที่สุดคือการกำหนดวิธีการทดสอบโค้ดที่เขียนอย่างต่อเนื่องและได้รับการทดสอบอย่างดีและสามารถทดสอบได้โดยไม่ทำลาย encapsulation หลักการของ BDD แนะนำให้เรามุ่งเน้นไปที่พฤติกรรมเกือบทั้งหมดและในบางวิธีดูเหมือนว่าจะแนะนำว่าคุณไม่จำเป็นต้องกังวลเกี่ยวกับรายละเอียดด้านในถึงขนาดใหญ่ แต่สิ่งนี้อาจทำให้การทดสอบค่อนข้างยากหากมี วิธีการส่วนตัวมากมายที่ทำ "สิ่ง" ในวิธีที่ซ่อนเร้นเพราะมันสามารถเพิ่มความซับซ้อนโดยรวมของการทดสอบของคุณเพื่อจัดการกับผลลัพธ์ที่เป็นไปได้ทั้งหมดในระดับสาธารณะมากขึ้น

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

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

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

ในกรณีที่ฉันพบรหัส"ไม่สามารถทดสอบได้"ฉันจะดูว่าฉันสามารถปรับโครงสร้างใหม่เพื่อให้สามารถทดสอบได้มากขึ้นหรือไม่ ที่ซึ่งคุณมีรหัสส่วนตัวมากมายซ่อนอยู่เบื้องหลังสิ่งต่าง ๆ บ่อยครั้งที่คุณจะพบว่ามีคลาสใหม่ ๆ คลาสเหล่านี้อาจใช้ภายใน แต่มักจะสามารถทดสอบได้อย่างอิสระโดยมีพฤติกรรมส่วนตัวน้อยกว่าและมักจะมีการเข้าถึงและความซับซ้อนน้อยกว่า สิ่งหนึ่งที่ฉันต้องระวังเพื่อหลีกเลี่ยงคือการเขียนรหัสการผลิตด้วยรหัสการทดสอบในตัวมันสามารถดึงดูดให้สร้าง " test lugs " ที่ส่งผลให้เกิดความน่าสะพรึงกลัวเช่นif testing then ...นี้ซึ่งบ่งชี้ว่าปัญหาการทดสอบไม่สมบูรณ์

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


1

ภาษาที่คุณใช้มีวิธี "จำลอง" วัตถุสำหรับทดสอบหรือไม่ ถ้าเป็นเช่นนั้นอินเทอร์เฟซที่น่ารำคาญเหล่านี้สามารถหายไปได้

ในบันทึกอื่นอาจมีเหตุผลที่จะมี SimpleInterface และ ComplexThing แต่เพียงผู้เดียวที่ใช้งาน อาจมีชิ้นส่วนของ ComplexThing ที่คุณไม่ต้องการให้ผู้ใช้ SimpleInterface เข้าถึงได้ ไม่ใช่เพราะ coder OO-ish มากเกินไป

ฉันจะก้าวออกไปตอนนี้และให้ทุกคนกระโดดข้ามความจริงที่ว่ารหัสนี้ "กลิ่นไม่ดี" กับพวกเขา


ใช่ฉันทำงานเป็นภาษาด้วยกรอบการเยาะเย้ยที่สนับสนุนการเยาะเย้ยวัตถุที่เป็นรูปธรรม เครื่องมือเหล่านี้ต้องการองศาการกระโดดผ่านห่วงที่แตกต่างกัน
Erik Dietrich

0

ฉันจะตอบในสองส่วน:

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

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


0

ฉันเห็นข้อดีหลายอย่างตั้งแต่เริ่มเขียนโปรแกรมเพิ่มเติมไปยังส่วนต่อประสานที่นอกเหนือจาก polymorphism

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

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

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

โดยรวมแล้วฉันเชื่อว่ามันช่วยให้ฉันเขียนรหัสที่มีคุณภาพสูงขึ้น

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