ฟังก์ชั่นและ / หรือคลาสใดเป็นไปไม่ได้ที่จะทดสอบหน่วยและสาเหตุ


21

ข้อแก้ตัวหลักจากนักพัฒนาที่ไม่มีการทดสอบหน่วยที่ดีคือ "รหัสไม่ได้ถูกออกแบบในแบบทดสอบได้" ฉันพยายามที่จะเข้าใจประเภทของการออกแบบและรหัสที่ไม่สามารถทดสอบหน่วยได้


2
ข้อแก้ตัวของคุณ? เพื่อนร่วมงานหรือไม่ ผู้จัดการ? คุณทำงานกับภาษา / กรอบอะไร

1
รหัสดั้งเดิมจำนวนมากในแอปพลิเคชันและไม่มีเวลาสำหรับการออกแบบใหม่
Knut

4
@gnat: ฉันไม่เห็นด้วย คำถามที่คุณอ้างถึงนั้นเกี่ยวกับสถานการณ์ที่การทดสอบหน่วยไม่มีประโยชน์ คำถามปัจจุบันเกี่ยวกับสถานการณ์ที่ทำให้การทดสอบหน่วยทำได้ยาก
Arseni Mourzenko

@MainMa เห็นได้ชัดว่าเราอ่านคำถามที่แตกต่างกัน "ฉันพยายามเข้าใจประเภทของการออกแบบและรหัสที่ไม่สามารถทดสอบหน่วยได้" => "เมื่อใดที่เหมาะสมที่จะไม่ทำการทดสอบหน่วย?"
ริ้น

1
@manizzzz: คุณอาจต้องการแก้ไขมันในคำถาม
jmoreno

คำตอบ:


27

มีหลายปัจจัยที่อาจทำให้รหัสทดสอบได้ยาก ในกรณีนี้การปรับโครงสร้างใหม่จะช่วยในการปรับปรุงรหัสเพื่อให้สามารถทดสอบได้

ตัวอย่างของโค้ดที่อาจทดสอบได้ยาก:

  • ฟังก์ชั่น 1,000-LOC
  • รหัสที่พึ่งพารัฐระดับโลกอย่างมาก
  • รหัสที่ต้องมีรูปธรรมซับซ้อนในการสร้างวัตถุเช่นบริบทฐานข้อมูลแทนที่จะพึ่งพาส่วนต่อประสานและการพึ่งพาการพึ่งพา
  • รหัสที่มีประสิทธิภาพอย่างช้าๆ ,
  • รหัสสปาเก็ตตี้
  • รหัสดั้งเดิมที่ได้รับการแก้ไขมานานหลายปีโดยไม่ต้องกังวลเกี่ยวกับความสามารถในการอ่านหรือการบำรุงรักษา
  • ยากที่จะเข้าใจรหัสที่มีความคิดเห็นหรือคำแนะนำเกี่ยวกับความตั้งใจเดิมของผู้เขียน function pGetDp_U(int i, int i2, string sText)(รหัสตัวอย่างซึ่งใช้ชื่อตัวแปรเช่น

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


8
นอกจากนี้ยังเป็นการยากที่จะทดสอบโค้ดที่ไม่ใช้การพึ่งพาฟังก์ชั่นที่ไม่บริสุทธิ์เช่นตัวเลขสุ่มเวลาปัจจุบัน I / O แบบใช้สายยาก ฯลฯ
9000

มันเป็นเรื่องเล็กน้อยที่จะทดสอบโค้ดเช่นนั้น - คุณเพียงแค่ต้องใช้เครื่องมือทดสอบที่ถูกต้องเท่านั้น ลอง Microsoft Fakes เป็นตัวอย่าง
gbjbaanb

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

14

มีหลายสิ่งที่ทำให้การทดสอบรหัสเป็นเรื่องยาก หลายคนบังเอิญทำให้รหัสยากที่จะรักษา:

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

ฉันคิดว่านี่เป็นคำตอบที่ยอดเยี่ยม คุณสัมผัสกับปัญหาระดับรหัสและปัญหาสถานะทั่วโลก @MainMa มีปัญหาอื่น ๆ ที่ฉันคิดว่าถูกต้อง แต่มีความหมายน้อยกว่า Jeffery Thomas กล่าวถึง I / O และ UI ฉันคิดว่าถ้าคุณเพิ่มส่วนที่ดีของคำตอบทั้งสามนี้คุณจะได้รับการตอบกลับที่ดีเยี่ยม ฉันชอบคำตอบนี้ดีที่สุดเพราะมุ่งเน้นไปที่รหัส antipatterns
M2tM

1
Argh - ไม่มีอะไรเลวร้ายไปกว่าการทดสอบหน่วยที่ไม่มีความคล้ายคลึงกับความต้องการทางธุรกิจและเป็นเพียงผลลัพธ์ในเวลาที่กำหนด - mocks ที่มีการตั้งค่าที่จะเรียก 3 ครั้งเช่น? ทำไมต้อง 3 เพราะมันเป็น 3 ครั้งแรกที่การทดสอบรัน / พูดจาโผงผาง :)
Michael

การมีเพศสัมพันธ์อย่างแน่นหนาจะไม่ดีก็ต่อเมื่อมันไม่เหมาะสม การมีเพศสัมพันธ์อย่างแน่นหนาในโค้ดที่มีความเหนียวแน่นเป็นสิ่งจำเป็น ตัวอย่างเช่นการประกาศตัวแปรตามด้วยการใช้งาน เชื่อมโยงแน่นและแน่นหนา
dietbuddha

1
การทดสอบหน่วยที่ผลลัพธ์ถูกตรวจสอบกับสิ่งที่รหัสทำโดยไม่มีกรณีศึกษา / เหตุผลทางธุรกิจใด ๆ ที่เรียกว่า Characterization Tests พวกมันถูกใช้ในการบำรุงรักษาซึ่งก่อนหน้านี้ไม่มีการทดสอบและบ่อยครั้งที่ไม่มีข้อกำหนดด้านเอกสารและคุณต้องใส่อะไรลงไปซึ่งจะแตกถ้าผลลัพธ์ของฟังก์ชันนั้นเปลี่ยนไป พวกเขาดีกว่าไม่มีอะไร
Andy Krouwel

5

ตัวอย่างทั่วไปของผู้ใช้รหัสไม่ต้องการทดสอบหน่วย:

  • โค้ดที่โต้ตอบกับ i / o โดยตรง (อ่านไฟล์, โทรผ่านเครือข่ายโดยตรง, ... )
  • รหัสที่อัปเดต UI โดยตรง
  • รหัสที่อ้างอิง singletons หรือวัตถุโกลบอลโดยตรง
  • รหัสซึ่งเปลี่ยนแปลงวัตถุหรือสถานะวัตถุย่อยโดยปริยาย

โดยใช้กรอบการจำลองตัวอย่างทั้งหมดเหล่านี้สามารถทดสอบหน่วย เป็นเพียงการตั้งค่าการเปลี่ยนจำลองสำหรับการอ้างอิงภายใน

สิ่งที่ไม่สามารถทดสอบหน่วยได้อย่างแท้จริง:

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

2

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

เขียนโค้ดที่ออกแบบมาไม่ดี

  • การมีเพศสัมพันธ์ที่ไม่เหมาะสม(มักจะมีเพศสัมพันธ์อย่างแน่นหนาไม่ควร)
  • รหัส sink ครัว(ที่ฟังก์ชันมีตรรกะ / ความรับผิดชอบมากเกินไป)

การพึ่งพาของรัฐในขอบเขตที่แตกต่างกัน

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

  • singletons
  • Globals
  • ปิด

สถานะภายนอก / ระบบ

  • การพึ่งพาฮาร์ดแวร์ / อุปกรณ์
  • การพึ่งพาเครือข่าย
  • การพึ่งพาระบบไฟล์
  • การพึ่งพาระหว่างกระบวนการ
  • การอ้างอิงการเรียกใช้ระบบอื่น

เห็นพ้องด้วย

  • เกลียว (ล็อคส่วนที่สำคัญ ฯลฯ )
  • fork'ing
  • coroutines
  • โทรกลับ
  • ตัวจัดการสัญญาณ (ไม่ใช่ทั้งหมด แต่มีบางตัว)

2

ไม่มีสิ่งใดที่เป็นรหัสที่ไม่สามารถทดสอบได้ อย่างไรก็ตามมีบางตัวอย่างของรหัสที่เป็นจริงยากที่จะทดสอบ (จนถึงจุดที่อาจไม่คุ้มค่าความพยายาม):

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

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


0

สามกลุ่มหลักของฉันคือ:

  • รหัสที่อาศัยบริการภายนอก

  • ระบบที่ไม่อนุญาตให้ผู้ทดสอบทำการแก้ไขสถานะโดยไม่ขึ้นกับแอปพลิเคชัน

  • สภาพแวดล้อมการทดสอบที่ไม่ทำซ้ำการตั้งค่าการผลิต

นี่คือสิ่งที่ฉันได้รับประสบการณ์มากที่สุดในฐานะนักพัฒนาที่เปลี่ยนวิศวกร QA

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