การทดสอบหน่วย C ++: ต้องทดสอบอะไร


20

TL; DR

การเขียนแบบทดสอบที่ดีและมีประโยชน์นั้นยากและมีค่าใช้จ่ายสูงในภาษาซีพลัสพลัส นักพัฒนาที่มีประสบการณ์คุณสามารถแบ่งปันเหตุผลของคุณเกี่ยวกับอะไรและเวลาที่จะทดสอบ?

เรื่องยาว

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

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

ตอนนี้ฉันได้รับโปรเจ็กต์ใหม่และฉันสงสัยว่าจะลองทำอย่างไร มันเป็นแอพพลิเคชั่น C ++ / OpenGL ดั้งเดิมดังนั้นการทดสอบการรวมเข้าด้วยกันจึงไม่ใช่ทางเลือก แต่การทดสอบหน่วยใน C ++ นั้นค่อนข้างยากกว่าใน Java (คุณต้องทำสิ่งต่าง ๆ อย่างชัดเจนvirtual) และโปรแกรมไม่ได้เน้นวัตถุมากดังนั้นฉันจึงไม่สามารถเยาะเย้ย / ทำอะไรบางอย่างได้

ฉันไม่ต้องการแยกออกจากกันและ OO-ize ทุกสิ่งเพียงเพื่อเขียนการทดสอบเพื่อประโยชน์ในการเขียนการทดสอบ ดังนั้นฉันถามคุณ: ฉันควรเขียนทดสอบอะไร เช่น:

  • ฟังก์ชั่น / คลาสที่ฉันคาดว่าจะเปลี่ยนบ่อย?
  • ฟังก์ชั่น / คลาสที่ยากต่อการทดสอบด้วยตนเอง?
  • ฟังก์ชั่น / คลาสที่ง่ายต่อการทดสอบ?

ฉันเริ่มตรวจสอบฐานรหัส C ++ ที่น่านับถือเพื่อดูว่าพวกเขาดำเนินการทดสอบอย่างไร ตอนนี้ฉันกำลังดูโครเมี่ยมซอร์สโค้ด แต่ฉันพบว่ามันยากที่จะแยกเหตุผลการทดสอบออกจากโค้ด หากใครมีตัวอย่างที่ดีหรือโพสต์ว่าผู้ใช้ C ++ ยอดนิยม (คนจากคณะกรรมการผู้แต่งหนังสือ Google, Facebook, Microsoft, ... ) เข้าหาสิ่งนี้ได้อย่างไรนั่นจะเป็นประโยชน์อย่างมาก

ปรับปรุง

ฉันได้ค้นหาเส้นทางของฉันรอบ ๆ ไซต์นี้และเว็บตั้งแต่เขียนสิ่งนี้ พบสิ่งที่ดี:

น่าเสียดายที่ทั้งหมดนี้เป็น Java / C # centric การเขียนการทดสอบจำนวนมากใน Java / C # ไม่ใช่ปัญหาใหญ่ดังนั้นประโยชน์มักจะมีมากกว่าค่าใช้จ่าย

แต่อย่างที่ฉันเขียนข้างต้นมันยากกว่าใน C ++ โดยเฉพาะอย่างยิ่งหากฐานรหัสของคุณไม่ดีดังนั้นคุณต้องทำเรื่องยุ่ง ๆ เพื่อให้ครอบคลุมการทดสอบหน่วยที่ดี ตัวอย่างเช่น: แอปพลิเคชันที่ฉันสืบทอดมามีGraphicsพื้นที่ชื่อที่เป็นเลเยอร์บาง ๆ เหนือ OpenGL เพื่อที่จะทดสอบเอนทิตีใด ๆ - ซึ่งทั้งหมดใช้ฟังก์ชั่นโดยตรง - ฉันต้องเปลี่ยนมันให้เป็นอินเทอร์เฟซและคลาสและฉีดเข้าไปในเอนทิตีทั้งหมด นั่นเป็นเพียงตัวอย่างเดียว

ดังนั้นเมื่อตอบคำถามนี้โปรดจำไว้ว่าฉันต้องลงทุนค่อนข้างมากสำหรับการเขียนข้อสอบ


3
+1 สำหรับความยากลำบากในการทดสอบหน่วย C ++ หากการทดสอบหน่วยของคุณต้องการให้คุณเปลี่ยนรหัส
DPD

2
@DPD: ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้นถ้ามีบางสิ่งที่คุ้มค่าในการทดสอบจริง ๆ ในฐานรหัสปัจจุบันฉันแทบจะไม่สามารถทดสอบสิ่งใดในรหัสจำลองเนื่องจากมันเรียกฟังก์ชันกราฟิกโดยตรงทั้งหมดและฉันไม่สามารถเยาะเย้ย / ทำให้ตกใจพวกเขา ทั้งหมดที่ฉันสามารถทดสอบได้ในตอนนี้คือฟังก์ชั่นยูทิลิตี้ แต่ฉันเห็นด้วยการเปลี่ยนรหัสเพื่อให้ "ทดสอบ" รู้สึกผิด ... ผู้เสนอ TDD มักจะบอกว่าสิ่งนี้จะทำให้โค้ดของคุณดีขึ้นในทุกวิถีทางเท่าที่จะเป็นไปได้ แต่ฉันไม่เห็นด้วยอย่างถ่อมใจ ไม่ใช่ทุกสิ่งที่ต้องการอินเทอร์เฟซและการใช้งานหลายอย่าง
futlib

ฉันขอยกตัวอย่างให้คุณฟัง: ฉันใช้เวลาหนึ่งวันทั้งวันในการทดสอบฟังก์ชั่นหนึ่งอย่าง (เขียนด้วยภาษา C ++ / CLI) และเครื่องมือทดสอบ MS Test มักจะผิดพลาดเสมอสำหรับการทดสอบนี้ ดูเหมือนว่าจะมีปัญหากับการอ้างอิง CPP ธรรมดา แต่ฉันเพิ่งทดสอบ ouptput ของฟังก์ชั่นการโทรและมันทำงานได้ดี ฉันเสียทั้งวันไปที่ UT หนึ่งฟังก์ชัน นั่นคือการสูญเสียเวลาอันมีค่า นอกจากนี้ฉันไม่สามารถรับเครื่องมือขัดที่เหมาะสมกับความต้องการของฉัน ฉันทำการขัดด้วยตนเองทุกครั้งที่ทำได้
DPD

นั่นเป็นเพียงสิ่งที่ฉันต้องการหลีกเลี่ยง: DI เดาว่าเราต้องพัฒนา c ++ devs อย่างจริงจังโดยเฉพาะอย่างยิ่งเกี่ยวกับการทดสอบ คุณทำการทดสอบเสร็จแล้วดังนั้นฉันเดาว่าไม่เป็นไร
futlib

@DPD: ฉันคิดเพิ่มเติมเกี่ยวกับเรื่องนี้และฉันคิดว่าคุณพูดถูกคำถามคือประเภทของการแลกเปลี่ยนที่ฉันต้องการทำ ควรปรับเปลี่ยนระบบกราฟิกทั้งหมดเพื่อทดสอบหน่วยงานสองหรือไม่ ไม่มีข้อบกพร่องใด ๆ ในนั้นฉันรู้ดังนั้นอาจ: ไม่ถ้ามันเริ่มรู้สึก buggy ฉันจะเขียนแบบทดสอบ น่าเสียดายที่ฉันไม่สามารถยอมรับคำตอบของคุณได้เพราะมันเป็นความเห็น :)
futlib

คำตอบ:


5

การทดสอบหน่วยเป็นเพียงส่วนหนึ่งเท่านั้น การทดสอบการรวมช่วยให้คุณมีปัญหากับทีมของคุณ การทดสอบการรวมสามารถเขียนได้สำหรับแอปพลิเคชันทุกประเภทรวมถึงแอปพลิเคชันดั้งเดิมและ OpenGL คุณควรตรวจสอบ "ซอฟต์แวร์การเติบโตเชิงวัตถุที่แนะนำโดยการทดสอบ" โดย Steve Freemann และ Nat Pryce (เช่นhttp://www.amazon.com/Growing-Object-Oriented-Software-Guided-Signature/dp/0321503627 ) มันนำคุณไปสู่การพัฒนาแอพพลิเคชั่นทีละขั้นตอนด้วย GUI และการสื่อสารเครือข่าย

การทดสอบซอฟต์แวร์ที่ไม่ได้ทดสอบขับเคลื่อนเป็นอีกเรื่องหนึ่ง ตรวจสอบ Michael Feathers "การทำงานอย่างมีประสิทธิภาพด้วยรหัสเดิม" (http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052)


ฉันรู้หนังสือทั้งสองเล่ม สิ่งต่าง ๆ คือ: 1. เราไม่ต้องการไปกับ TDD เพราะมันทำงานได้ไม่ดีกับเรา เราต้องการทดสอบ แต่ไม่เคร่งศาสนา 2. ฉันแน่ใจว่าการทดสอบการรวมสำหรับแอปพลิเคชัน OpenGL เป็นไปได้ แต่ก็ต้องใช้ความพยายามมากเกินไป ฉันต้องการปรับปรุงแอปต่อไปไม่ใช่เริ่มต้นโครงการวิจัย
futlib

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

ฉันเห็นด้วยว่ารหัสที่ไม่ใช่ TDD นั้นไม่ได้ออกแบบมาให้สามารถทดสอบได้ แต่ฉันจะไม่บอกว่ามันไม่สามารถบำรุงรักษาได้หรือสามารถนำมาใช้ซ้ำได้ - ในขณะที่ฉันแสดงความคิดเห็นข้างต้นบางสิ่งก็ไม่ต้องการอินเทอร์เฟซ ไม่ใช่ปัญหากับ Mockito เลย แต่ใน C ++ ฉันต้องทำให้ทุกฟังก์ชั่นที่ฉันต้องการ stub / mock virtual อย่างไรก็ตามรหัสที่ไม่สามารถทดสอบได้เป็นปัญหาที่ใหญ่ที่สุดของฉันในตอนนี้: ฉันต้องเปลี่ยนสิ่งพื้นฐานบางอย่างเพื่อให้บางส่วนทดสอบได้และดังนั้นฉันต้องการเหตุผลที่ดีในการทดสอบเพื่อให้แน่ใจว่ามันคุ้มค่า
futlib

คุณถูกต้องแน่นอนฉันจะระวังทำรหัสใหม่ที่ฉันเขียนได้ แต่มันจะไม่ง่ายด้วยวิธีการทำงานของสิ่งต่าง ๆ ในฐานรหัสนี้ในตอนนี้
futlib

เมื่อคุณเพิ่มคุณสมบัติ / ฟังก์ชั่นลองคิดดูว่าคุณจะทดสอบได้อย่างไร คุณสามารถฉีดการพึ่งพาที่น่าเกลียดใด ๆ ได้หรือไม่? คุณจะรู้ได้อย่างไรว่าฟังก์ชั่นทำในสิ่งที่ควรทำ คุณสามารถสังเกตพฤติกรรมใด ๆ มีผลใดที่คุณสามารถตรวจสอบความถูกต้องได้หรือไม่? มีค่าคงที่ใดบ้างที่คุณสามารถตรวจสอบได้
EricSchaefer

2

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

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

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

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


4
เราทำ TDD มาประมาณหนึ่งปีครึ่งและทุกคนต่างก็หลงใหลมัน แต่การเปรียบเทียบโครงการ TDD กับโครงการที่ทำก่อนหน้านี้โดยไม่ใช้ TDD (แต่ไม่ได้ทำการทดสอบ) ฉันจะไม่พูดว่าพวกเขามีเสถียรภาพมากกว่าหรือมีรหัสที่ออกแบบมาดีกว่า อาจจะเป็นทีมของเรา: เราจับคู่และตรวจสอบเป็นจำนวนมากคุณภาพของรหัสของเราค่อนข้างดีอยู่เสมอ
futlib

1
ยิ่งฉันคิดเกี่ยวกับมันมากเท่าไหร่ฉันก็ยิ่งคิดว่า TDD ไม่เหมาะกับเทคโนโลยีของโครงการนั้น ๆ ด้วย: Flex / Swiz มีเหตุการณ์และการผูกและการฉีดที่เกิดขึ้นมากมายทำให้การโต้ตอบระหว่างวัตถุซับซ้อนและแทบเป็นไปไม่ได้ในการทดสอบหน่วย การแยกวัตถุเหล่านั้นออกไม่ได้ทำให้ดีขึ้นเพราะมันทำงานถูกต้องตั้งแต่แรก
futlib

2

ฉันไม่ได้ทำ TDD ใน C ++ ดังนั้นฉันจึงไม่สามารถให้ความเห็นเกี่ยวกับเรื่องนั้นได้ แต่คุณควรจะทดสอบพฤติกรรมที่คาดหวังของรหัสของคุณ ในขณะที่การใช้งานสามารถเปลี่ยนแปลงพฤติกรรมควร (ปกติ?) ยังคงเหมือนเดิม ใน Java \ C # centric world นั่นหมายความว่าคุณจะทดสอบเฉพาะวิธีการสาธารณะเขียนการทดสอบสำหรับพฤติกรรมที่คาดหวังและทำสิ่งนั้นก่อนที่จะนำไปใช้

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