TDD ทำให้การเขียนโปรแกรมเชิงป้องกันซ้ำซ้อนหรือไม่?


104

วันนี้ฉันมีการสนทนาที่น่าสนใจกับเพื่อนร่วมงาน

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

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

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


120
คุณมอบไลบรารี่ที่ผ่านการทดสอบแล้วโดยไม่มีการตรวจสอบคอนสตรัคเตอร์ให้กับลูกค้าเพื่อใช้และพวกเขาจะแบ่งสัญญาคลาส คุณทำแบบทดสอบหน่วยเหล่านี้ได้ดีแค่ไหน?
Robert Harvey

42
IMO เป็นวิธีอื่น ๆ การเขียนโปรแกรมเชิงป้องกันก่อนและเงื่อนไขที่เหมาะสมและระบบประเภทที่หลากหลายทำให้การทดสอบซ้ำซ้อน
Gardenhead

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

16
"การทดสอบหน่วยควรตรวจจับการใช้งานที่ไม่ถูกต้องของคลาส" - เอ่อได้อย่างไร การทดสอบหน่วยจะแสดงพฤติกรรมที่ให้ข้อโต้แย้งที่ถูกต้องและเมื่อได้รับข้อโต้แย้งที่ไม่ถูกต้อง พวกเขาไม่สามารถแสดงให้คุณเห็นข้อโต้แย้งทั้งหมดที่จะได้รับ
OJFord

34
ฉันไม่คิดว่าฉันได้เห็นตัวอย่างที่ดีกว่าของวิธีคิดที่ไร้เหตุผลเกี่ยวกับการพัฒนาซอฟต์แวร์สามารถนำไปสู่ข้อสรุปที่เป็นอันตราย
sdenham

คำตอบ:


196

นั่นไร้สาระ TDD บังคับให้รหัสผ่านการทดสอบและบังคับให้รหัสทั้งหมดมีการทดสอบรอบ ๆ มันไม่ได้ป้องกันผู้บริโภคของคุณจากการเรียกรหัสไม่ถูกต้องและไม่ได้ป้องกันการเขียนโปรแกรมที่ขาดหายไปอย่างน่าอัศจรรย์

ไม่มีวิธีการใดที่บังคับให้ผู้ใช้ใช้รหัสอย่างถูกต้อง

มีคืออาจจะโดยคุณเพิ่มการตรวจสอบ - การโต้เถียงเล็กน้อยที่จะทำว่าถ้าคุณได้อย่างสมบูรณ์แบบได้ TDD ที่คุณจะได้จับเช็ค> 0 ในกรณีการทดสอบก่อนที่จะมีการดำเนินการนั้นและการแก้ไขนี้ แต่ถ้าคุณทำ TDD, ความต้องการของคุณ (> 0 ในตัวสร้าง) จะเป็นครั้งแรกปรากฏเป็น testcase ที่ล้มเหลว ดังนั้นให้คุณทดสอบหลังจากที่คุณเพิ่มเช็คของคุณ

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

หรือว่าทั้งสองเทคนิคเติมเต็มซึ่งกันและกัน?

TDD จะพัฒนาการทดสอบ การใช้การตรวจสอบความถูกต้องของพารามิเตอร์จะทำให้พวกเขาผ่านไป


7
ฉันไม่เห็นด้วยกับความเชื่อที่ว่าควรมีการทดสอบการตรวจสอบสภาพก่อนกำหนด แต่ฉันไม่เห็นด้วยกับความคิดเห็นของเพื่อนร่วมงานของฉันว่างานพิเศษที่เกิดจากความจำเป็นในการทดสอบการตรวจสอบความถูกต้องเบื้องต้นนั้นเป็นข้อโต้แย้ง สถานที่. ฉันได้แก้ไขโพสต์ของฉันเพื่อชี้แจง
user2180613

20
@ user2180613 สร้างการทดสอบที่ทดสอบว่าจัดการความล้มเหลวของเงื่อนไขเบื้องต้นได้อย่างเหมาะสม: ตอนนี้การเพิ่มการตรวจสอบไม่ใช่งานพิเศษ "มันเป็นงานที่จำเป็นโดย TDD เพื่อทำการทดสอบสีเขียว หากความคิดเห็นของเพื่อนร่วมงานของคุณคือคุณควรทำการทดสอบสังเกตว่ามันล้มเหลวและจากนั้นใช้การตรวจสอบสภาพก่อนกำหนดเท่านั้นจากนั้นเขาอาจมีจุดจากจุดยืนของ TDD-purist หากเขาพูดเพียงเพื่อเพิกเฉยต่อการตรวจสอบอย่างสมบูรณ์แสดงว่าเขาโง่ ไม่มีอะไรใน TDD ที่บอกว่าคุณไม่สามารถเป็นเชิงรุกในการเขียนการทดสอบสำหรับโหมดความล้มเหลวที่อาจเกิดขึ้น
RM

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

@ user2180613 นั่นคือเหตุผลที่ยอดเยี่ยม: D ถ้าเป้าหมายของคุณในการเขียนซอฟต์แวร์คือคุณลดจำนวนการทดสอบที่คุณต้องเขียนและเรียกใช้อย่าเขียนซอฟต์แวร์ใด ๆ - ทดสอบเป็นศูนย์!
Gusdor

3
ประโยคสุดท้ายของคำตอบนี้ตอกย้ำมัน
Robert Grant

32

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

ในความปลอดภัยของข้อมูลนี้เรียกว่ากลาโหมในเชิงลึก การมีเลเยอร์การป้องกันหลายชั้นทำให้มั่นใจได้ว่าหากล้มเหลว

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


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

1
"มันเหมือนกับการทดสอบรหัสอื่น ๆ คุณต้องการให้แน่ใจว่าการใช้งานทั้งหมดแม้กระทั่งรหัสที่ไม่ถูกต้องจะมีผลลัพธ์ที่คาดหวัง" นี้. ไม่ควรใช้รหัสใด ๆ เมื่อผ่านการป้อนผ่านมันไม่ได้ออกแบบมาเพื่อจัดการ การทำเช่นนี้เป็นการฝ่าฝืนหลักการ "ล้มเหลวอย่างรวดเร็ว" และสามารถทำการดีบั๊กฝันร้ายได้
jpmc26

@ user2180613 - ไม่ใช่จริงๆ แต่มากกว่านั้นการทดสอบหน่วยตรวจสอบเงื่อนไขความล้มเหลวที่นักพัฒนาคาดว่าจะได้ในขณะที่เทคนิคการเขียนโปรแกรมเชิงป้องกันตรวจสอบเงื่อนไขที่นักพัฒนาไม่คาดหวัง การทดสอบหน่วยสามารถใช้ในการตรวจสอบความถูกต้องเบื้องต้น (โดยการใช้วัตถุจำลองที่ถูกส่งไปยังผู้โทรเพื่อตรวจสอบเงื่อนไขเบื้องต้น)
Periata Breatta

1
@ jpmc26 ใช่ความล้มเหลวคือ “ ผลลัพธ์ที่คาดหวัง” สำหรับการทดสอบ คุณทดสอบเพื่อแสดงว่ามันล้มเหลวแทนที่จะแสดงพฤติกรรมบางอย่างที่ไม่ได้กำหนด (ไม่คาดคิด) อย่างเงียบ ๆ
KRyan

6
TDD จับข้อผิดพลาดในรหัสของคุณโปรแกรมป้องกันจับข้อผิดพลาดในรหัสของคนอื่น TDD จึงสามารถช่วยให้แน่ใจว่าคุณกำลังป้องกันพอ :)
jwenting

30

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

ใน TDD คุณไม่ควรเขียนโค้ดโดยไม่ต้องเขียนการทดสอบก่อน - ตามวงจรสีแดง - เขียว - refactor อย่างเคร่งครัด นั่นหมายความว่าหากคุณต้องการเพิ่มการตรวจสอบความถูกต้องอันดับแรกให้เขียนการทดสอบก่อนที่ต้องการการตรวจสอบความถูกต้องนี้ โทรหาวิธีที่เป็นปัญหาด้วยตัวเลขติดลบและมีศูนย์และคาดว่าจะทำให้เกิดข้อยกเว้น

นอกจากนี้อย่าลืมขั้นตอน "refactor" ในขณะที่มีการ TDD test- ขับเคลื่อนนี้ไม่ได้หมายความว่า test- เท่านั้น คุณยังควรใช้การออกแบบที่เหมาะสมและเขียนรหัสที่เหมาะสม การเขียนโค้ดป้องกันเป็นโค้ดที่สมเหตุสมผลเนื่องจากจะทำให้การคาดหวังมีความชัดเจนมากขึ้นและรหัสของคุณโดยรวมมีประสิทธิภาพมากขึ้นการระบุข้อผิดพลาดที่เป็นไปได้ก่อนทำให้ง่ายต่อการดีบัก

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

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


16

การทดสอบพร้อมให้การสนับสนุนและรับประกันการโปรแกรมป้องกัน

การตั้งโปรแกรมการป้องกันป้องกันความสมบูรณ์ของระบบที่รันไทม์

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

แก้ไข: การเปรียบเทียบ

สิ่งที่เกี่ยวกับการเปรียบเทียบความคิดเห็นในรหัส?

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

ดังนั้นสมมติว่าคุณใส่ความรู้พื้นฐานของโค้ดในการทดสอบเช่น MethodA ไม่สามารถใช้ค่า Null และอาร์กิวเมนต์ของ MethodB ต้องเป็น> 0เช่นนั้น จากนั้นรหัสจะเปลี่ยน Null ไม่เป็นไรสำหรับ A ในขณะนี้และ B สามารถรับค่าขนาดเล็กเป็น -10 ขณะนี้การทดสอบที่มีอยู่ผิดปกติ แต่จะผ่านต่อไป

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

การทดสอบจะตรวจสอบพฤติกรรมของระบบ พฤติกรรมที่แท้จริงนั้นเป็นสิ่งที่แท้จริงต่อระบบนั้นเองไม่ใช่เพื่อการทดสอบ

สิ่งที่อาจจะผิดไป?

เป้าหมายที่เกี่ยวข้องกับการทดสอบคือการคิดทุกอย่างที่ผิดพลาดเขียนการทดสอบเพื่อตรวจสอบพฤติกรรมที่ถูกต้องจากนั้นจึงสร้างรหัสรันไทม์เพื่อให้ผ่านการทดสอบทั้งหมด

ซึ่งหมายความว่าการเขียนโปรแกรมการป้องกันเป็นจุด

TDD ไดรฟ์เขียนโปรแกรมป้องกันหากการทดสอบที่ครอบคลุม

การทดสอบเพิ่มเติมการขับขี่โปรแกรมการป้องกันที่มากขึ้น

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

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

พูดโดยทั่วไป ...

ตัวอย่างเช่นถ้าอาร์กิวเมนต์ null ของโพรซีเดอร์เฉพาะนั้นไม่ถูกต้องการทดสอบอย่างน้อยหนึ่งครั้งจะผ่านโมฆะและจะต้องมีข้อยกเว้น / ข้อผิดพลาดบางอย่าง

อย่างน้อยหนึ่งการทดสอบอื่น ๆ จะผ่านการโต้แย้งที่ถูกต้องแน่นอน - หรือวนซ้ำผ่านอาร์เรย์ขนาดใหญ่และผ่านการขัดแย้งที่ถูกต้องมากมายและยืนยันว่าสถานะผลลัพธ์นั้นเหมาะสม

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

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

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

โดยเฉพาะอย่างยิ่งหากโปรแกรมเมอร์ของระบบอื่นนั้นไม่ได้รับรหัสป้องกัน


2
นั่นตลกการโหวตลงมาเร็วมากตอนนี้มีวิธีที่ผู้อ่านสามารถอ่านได้เกินกว่าย่อหน้าแรก
Craig

1
:-) ฉันเพิ่งขึ้นลงมติโดยไม่ต้องอ่านเกินวรรคหนึ่งเพื่อหวังที่จะรักษาความสมดุลของมันออกมา ...
SusanW

1
ดูเหมือนน้อยฉันจะทำ :-) (อันที่จริงผมไม่อ่านส่วนที่เหลือเพียงเพื่อให้แน่ใจต้องไม่เลอะเทอะ -. โดยเฉพาะอย่างยิ่งในหัวข้อเช่นนี้)
SusanW

1
ฉันคิดว่าคุณอาจจะมี :)
Craig

การตรวจสอบเชิงป้องกันสามารถทำได้ในเวลารวบรวมด้วยเครื่องมือเช่นสัญญารหัส
Matthew Whited

9

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


ดังนั้นเนื่องจากเราทำการทดสอบซอฟต์แวร์เราควรเลิกแสดงข้อความยืนยันในรหัสการผลิตใช่ไหม ให้ฉันนับวิธีที่สิ่งนี้ผิด:

  1. การยืนยันเป็นทางเลือกดังนั้นหากคุณไม่ชอบให้เปิดใช้งานระบบโดยปิดการใช้งานการยืนยัน

  2. การยืนยันการตรวจสอบสิ่งต่าง ๆ ที่การทดสอบไม่สามารถทำได้ (และไม่ควร) เนื่องจากการทดสอบนั้นควรมีมุมมองกล่องดำในระบบของคุณในขณะที่การยืนยันนั้นมีมุมมองแบบกล่องสีขาว (แน่นอนเพราะพวกเขาอาศัยอยู่ในนั้น)

  3. การยืนยันเป็นเครื่องมือเอกสารที่ยอดเยี่ยม ไม่เคยมีใครแสดงความคิดเห็นหรือเคยเป็นอย่างที่คลุมเครือเหมือนโค้ดที่ยืนยันสิ่งเดียวกัน นอกจากนี้เอกสารมีแนวโน้มที่จะล้าสมัยเป็นรหัสวิวัฒนาการและมันก็ไม่ได้บังคับใช้โดยคอมไพเลอร์

  4. การยืนยันสามารถตรวจจับข้อผิดพลาดในรหัสการทดสอบ คุณเคยเจอสถานการณ์ที่การทดสอบล้มเหลวและคุณไม่รู้ว่าใครเป็นคนผิด - รหัสการผลิตหรือการทดสอบ?

  5. การยืนยันสามารถทำได้มากกว่าการทดสอบ การทดสอบจะตรวจสอบสิ่งที่กำหนดโดยความต้องการด้านการใช้งาน แต่บ่อยครั้งที่รหัสจะต้องมีการตั้งสมมติฐานบางอย่างที่เป็นเทคนิคมากไปกว่านั้น ผู้ที่เขียนเอกสารข้อกำหนดด้านการทำงานไม่ค่อยนึกถึงการหารด้วยศูนย์

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

  7. การยืนยันลดความซับซ้อนของโปรแกรม ทุกบรรทัดของโค้ดที่คุณเขียนจะเพิ่มความซับซ้อนของโปรแกรม คำยืนยันและคำสำคัญfinal( readonly) เป็นคำสั่งสร้างเพียงสองคำเท่านั้นที่ฉันรู้ว่านั่นช่วยลดความซับซ้อนของโปรแกรม นั่นเป็นสิ่งที่ประเมินค่ามิได้

  8. การยืนยันช่วยให้คอมไพเลอร์เข้าใจโค้ดของคุณได้ดีขึ้น โปรดลองทำที่บ้าน: void foo( Object x ) { assert x != null; if( x == null ) { } }คอมไพเลอร์ของคุณควรออกคำเตือนบอกคุณว่าสภาพx == nullเป็นเท็จเสมอ ที่มีประโยชน์มาก

ด้านบนเป็นบทสรุปของโพสต์จากบล็อกของฉัน, 2014-09-21 "การยืนยันและการทดสอบ"


ฉันคิดว่าฉันไม่เห็นด้วยกับคำตอบนี้เป็นส่วนใหญ่ (5) ใน TDD ชุดทดสอบเป็นข้อกำหนด คุณควรจะเขียนโค้ดที่ง่ายที่สุดที่ทำให้การทดสอบผ่านไม่มีอะไรเพิ่มเติม (4) เวิร์กโฟลว์สีแดงสีเขียวทำให้แน่ใจว่าการทดสอบล้มเหลวเมื่อมันควรและผ่านเมื่อมีฟังก์ชั่นที่ตั้งใจไว้ การยืนยันไม่ได้ช่วยอะไรมากที่นี่ (3,7) เอกสารเป็นเอกสารรับรองไม่ได้ แต่ด้วยการทำให้สมมติฐานชัดเจนรหัสจะกลายเป็นเอกสารด้วยตนเอง ฉันคิดว่าพวกเขาเป็นความคิดเห็นที่ปฏิบัติการได้ (2) การทดสอบกล่องขาวสามารถเป็นส่วนหนึ่งของกลยุทธ์การทดสอบที่ถูกต้อง
amon

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

5

ฉันเชื่อว่าคำตอบส่วนใหญ่ขาดความแตกต่างที่สำคัญ: ขึ้นอยู่กับว่าจะใช้โค้ดของคุณอย่างไร

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

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

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


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

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

3
สิ่งนี้จะทำงานในร้านค้าเล็กที่สุดเท่านั้น เมื่อทีมของคุณได้รับมากกว่าพูดอีกหกคนคุณจะต้องตรวจสอบความถูกต้อง
Robert Harvey

1
@RobertHarvey: ในกรณีนี้ระบบควรจะแบ่งพาร์ติชันเป็นระบบย่อยที่มีอินเตอร์เฟสที่กำหนดไว้อย่างดีและการตรวจสอบอินพุตที่ดำเนินการที่อินเตอร์เฟส
JacquesB

นี้. ขึ้นอยู่กับรหัสรหัสนี้จะใช้โดยทีมหรือไม่ ทีมมีการเข้าถึงซอร์สโค้ดหรือไม่ หากรหัสภายในหมดจดแล้วตรวจสอบข้อโต้แย้งอาจเป็นภาระเช่นคุณตรวจสอบ 0 แล้วโยนข้อยกเว้นและผู้โทรแล้วดูเป็นรหัสโอ้คลาสนี้สามารถโยนข้อยกเว้น ฯลฯ ฯลฯ และรอ .. ในกรณีนี้ว่า วัตถุจะไม่ได้รับ 0 เนื่องจากถูกกรองออกไป 2 lvls ก่อน หากนั่นเป็นรหัสห้องสมุดที่จะใช้โดยบุคคลที่สามนั่นเป็นอีกเรื่องหนึ่ง ไม่มีรหัสทั้งหมดที่ถูกเขียนขึ้นเพื่อให้ใช้ทั่วโลก
Aleksander Fular

3

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

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

กล่าวอีกนัยหนึ่งสิ่งที่ TDD ไม่ได้ป้องกันไม่ให้คุณต้องใช้รหัสการตรวจสอบความถูกต้องเท่าที่ช่วยคุณหลีกเลี่ยงการลืมมัน


2

ฉันคิดว่าฉันตีความคำพูดของเพื่อนร่วมงานของคุณแตกต่างจากคำตอบที่เหลือ

ดูเหมือนว่าฉันจะโต้แย้งว่า:

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

สำหรับฉันแล้วข้อโต้แย้งนี้มีตรรกะบางอย่าง แต่เชื่อมั่นมากเกินไปในการทดสอบหน่วยเพื่อครอบคลุมทุกสถานการณ์ที่เป็นไปได้ ความจริงง่ายๆคือการครอบคลุม 100% line / branch / path นั้นไม่จำเป็นต้องใช้ทุกค่าที่ผู้เรียกอาจส่งผ่านในขณะที่ความครอบคลุม 100% ของสถานะที่เป็นไปได้ทั้งหมดของผู้เรียก (กล่าวคือค่าที่เป็นไปได้ทั้งหมดของอินพุต และตัวแปร) เป็นไปไม่ได้ที่คำนวณได้

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

โปรดทราบว่าหากคุณบันทึกและทดสอบการทำงานของฟังก์ชันของคุณเมื่อมีการส่งผ่านค่า <= 0 ค่าลบจะไม่ถูกต้องอีกต่อไป (อย่างน้อยก็ไม่ถูกต้องมากกว่าอาร์กิวเมนต์ใด ๆ เลยthrowตั้งแต่นั้นเป็นต้นมา มีการบันทึกไว้เพื่อยกเว้นข้อผิดพลาด!) ผู้โทรมีสิทธิ์พึ่งพาพฤติกรรมการป้องกัน ภาษาเอื้ออำนวยก็อาจจะเป็นได้ว่านี่คือในกรณีใด ๆ สถานการณ์ที่ดีที่สุด - ฟังก์ชั่นมีไม่ "ปัจจัยการผลิตที่ไม่ถูกต้อง" แต่สายที่ไม่ได้คาดหวังที่จะกระตุ้นการทำงานลงในการขว้างปายกเว้นควรจะเป็นหน่วยงานที่ผ่านการทดสอบอย่างเพียงพอเพื่อให้แน่ใจว่าพวกเขาสวม' ไม่ส่งผ่านค่าใด ๆ ที่ทำให้เกิด

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


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

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

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

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

1

ส่วนต่อประสานสาธารณะสามารถและจะถูกนำไปใช้ในทางที่ผิด

การอ้างสิทธิ์ผู้ร่วมงานของคุณ "การทดสอบหน่วยควรตรวจจับการใช้งานในชั้นเรียนที่ไม่ถูกต้อง" เป็นเท็จอย่างยิ่งสำหรับอินเทอร์เฟซที่ไม่เป็นส่วนตัว หากฟังก์ชั่นสาธารณะสามารถเรียกว่ามีข้อโต้แย้งจำนวนเต็มมันสามารถและจะถูกเรียกด้วยข้อโต้แย้งจำนวนเต็มใด ๆและรหัสควรทำงานอย่างเหมาะสม หากลายเซ็นฟังก์ชั่นสาธารณะยอมรับเช่นประเภท Java Double แล้ว null, NaN, MAX_VALUE, -Inf เป็นค่าที่เป็นไปได้ทั้งหมด การทดสอบหน่วยของคุณไม่สามารถใช้งานคลาสได้อย่างไม่ถูกต้องเนื่องจากการทดสอบเหล่านั้นไม่สามารถทดสอบรหัสที่จะใช้คลาสนี้ได้เนื่องจากรหัสนั้นยังไม่ได้เขียนคุณอาจไม่ได้เขียนและจะอยู่นอกขอบเขตการทดสอบหน่วยของคุณ .

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


หัวเรื่องและย่อหน้าแรกของเรื่องนี้เป็นจริงเพราะไม่ใช่การทดสอบหน่วยที่จะใช้รหัสในการใช้งานจริง มันเป็นรหัสรันไทม์อื่น ๆและการเปลี่ยนแปลงเงื่อนไขในโลกแห่งความจริงและการป้อนข้อมูลของผู้ใช้ที่ไม่ดีและความพยายามในการแฮ็คมีปฏิสัมพันธ์กับรหัส
Craig

1

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

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

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

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

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

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

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

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

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

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


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

1

การทดสอบกำหนดสัญญาของชั้นเรียนของคุณ

ในฐานะที่เป็นข้อพิสูจน์ที่ขาดของการทดสอบกำหนดสัญญาที่มีลักษณะการทำงานที่ไม่ได้กำหนด ดังนั้นเมื่อคุณผ่านnullไปFoo::Frobnicate(Widget widget)และความเสียหายที่เกิดขึ้นในเวลาไม่ได้บอกเล่าคุณยังอยู่ในสัญญาของชั้นเรียนของคุณ

หลังจากนั้นคุณตัดสินใจว่า "เราไม่ต้องการความเป็นไปได้ของพฤติกรรมที่ไม่ได้กำหนด" ซึ่งเป็นตัวเลือกที่สมเหตุสมผล นั่นหมายความว่าคุณต้องมีพฤติกรรมที่คาดหวังสำหรับการส่งผ่านไปยังnullFoo::Frobnicate(Widget widget)

และคุณบันทึกเอกสารการตัดสินใจโดยรวม

[Test]
void Foo_FrobnicatesANullWidget_ThrowsInvalidArgument() 
{
    Given(Foo foo);
    When(foo.Frobnicate(null));
    Then(Expect_Exception(InvalidArgument));
}

1

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

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

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


0

การทดสอบของ TDD จะจับความผิดพลาดในระหว่างการพัฒนาของรหัส

ขอบเขตการตรวจสอบคุณอธิบายเป็นส่วนหนึ่งของการเขียนโปรแกรมการป้องกันจะจับความผิดพลาดในระหว่างการใช้งานของรหัส

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


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

แต่หลังจากที่ห้องสมุดเปิดตัวและฉันใช้มันในโปรแกรมของฉันเองการทดสอบ TDD เหล่านั้นไม่ได้ป้องกันฉันจากการกำหนดค่าลบ (สมมติว่ามีการเปิดเผย) การตรวจสอบขอบเขตจะ

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


1
ฉันไม่ได้ลงคะแนน แต่ฉันเห็นด้วยกับ downvote บนสมมติฐานที่เพิ่มความแตกต่างที่ลึกซึ้งให้กับการโต้เถียงแบบนี้ทำให้น้ำขุ่น
Craig

@ Craig ฉันจะสนใจข้อเสนอแนะของคุณในตัวอย่างที่ฉันเพิ่ม
Blackhawk

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

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

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

0

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

ในฐานะวิศวกรที่ฝังตัวฉันชอบที่จะใช้ตัวอย่างของการเขียนฟังก์ชั่นเพื่อเพิ่มสองไบต์ด้วยกันและส่งคืนผลลัพธ์ดังนี้:

uint8_t AddTwoBytes(uint8_t a, uint8_t b, uint8_t *sum); 

ตอนนี้ถ้าคุณเพียงแค่ทำ*(sum) = a + bมันจะได้ผล แต่มีเพียงบางอินพุตเท่านั้น a = 1และb = 2จะทำให้sum = 3; อย่างไรก็ตามเนื่องจากขนาดของผลรวมเป็นไบต์a = 100และb = 200จะทำsum = 44เนื่องจากล้น ใน C คุณจะส่งคืนข้อผิดพลาดในกรณีนี้เพื่อแสดงว่าฟังก์ชันล้มเหลว การโยนข้อยกเว้นเป็นสิ่งเดียวกันในรหัสของคุณ การไม่พิจารณาความล้มเหลวหรือการทดสอบวิธีจัดการกับมันจะไม่ทำงานในระยะยาวเพราะหากเงื่อนไขเหล่านั้นเกิดขึ้นพวกเขาจะไม่ได้รับการจัดการและอาจทำให้เกิดปัญหาจำนวนเท่าใดก็ได้


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