การเขียนโปรแกรมตามสัญญาและการทดสอบหน่วย


13

ฉันเป็นโปรแกรมเมอร์ที่ค่อนข้างป้องกันและเป็นแฟนตัวยงของ Microsoft Code Contracts

ตอนนี้ฉันไม่สามารถใช้ C # และในภาษาส่วนใหญ่เครื่องมือเดียวที่ฉันมีคือการยืนยัน ดังนั้นฉันมักจะจบลงด้วยรหัสเช่นนี้:

class
{       
    function()
    {   
         checkInvariants();
         assert(/* requirement */);

         try
         {
             /* implementation */
         }
         catch(...)
         {
              assert(/* exceptional ensures */);                  
         }
         finally
         {
              assert(/* ensures */);
              checkInvariants();
         }
    }

    void checkInvariants()
    {
         assert(/* invariant */);
    }
}

อย่างไรก็ตามกระบวนทัศน์นี้ (หรือสิ่งที่คุณจะเรียกว่า) นำไปสู่ความยุ่งเหยิงของรหัสจำนวนมาก

ฉันเริ่มสงสัยว่ามันคุ้มค่ากับความพยายามจริง ๆ หรือไม่และการทดสอบหน่วยที่เหมาะสมจะครอบคลุมเรื่องนี้หรือไม่?


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

ดังนั้นโดยทั่วไปเวลาในการพัฒนาความสามารถในการบำรุงรักษาความสามารถในการอ่านและการครอบคลุมโค้ดที่ดีกว่า
ronag

1
ฉันส่วนใหญ่ใช้การยืนยันในโค้ดเพื่อตรวจสอบความถูกต้องของพารามิเตอร์ (เช่นค่าว่าง) และในการทดสอบหน่วยเพิ่มเติมตรวจสอบการยืนยันเหล่านั้น
artjom

คำตอบ:


14

ฉันไม่คิดว่าคุณควรคิดว่ามันเป็น "vs"
ดังที่ได้กล่าวไว้ในความคิดเห็นโดย @Giorgio สัญญารหัสจะต้องตรวจสอบค่าคงที่ (ในสภาพแวดล้อมการผลิต) และการทดสอบหน่วยเพื่อให้แน่ใจว่ารหัสของคุณทำงานตามที่คาดไว้เมื่อตรงตามเงื่อนไขเหล่านั้น


2
ฉันคิดว่ามันเป็นสิ่งสำคัญที่จะทดสอบว่าการทำงานของรหัส (เช่นมันจะพ่นข้อยกเว้น) เมื่อเงื่อนไขจะไม่ได้พบกัน
svick

6

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

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

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


1

ตามที่กล่าวไว้แล้วสัญญาและการทดสอบหน่วยมีวัตถุประสงค์ที่แตกต่างกัน

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

การทดสอบหน่วยเพื่อให้แน่ใจว่ารหัสทำงานในสถานการณ์ที่แตกต่างกัน เหล่านี้เป็นเหมือน 'รายละเอียดด้วยฟัน'

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


0

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

ฉันเห็นด้วยกับ @duros สิ่งเหล่านี้ไม่ควรถูกมองว่าเป็นวิธีการพิเศษหรือการแข่งขัน ในความเป็นจริงในสภาพแวดล้อม TDD คุณยังสามารถยืนยันว่า 'ข้อกำหนด' การยืนยันจะต้องมีการทดสอบ :)

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

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

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

นอกจากนี้ฉันอยากรู้ว่าภาษาใดที่คุณใช้ซึ่งไม่มี xUnit หรือห้องสมุดทดสอบอื่น ๆ ที่มีคนพัฒนาฉันคิดว่ามันมีอะไรบางอย่างในทุกวันนี้


0

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

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

Anders Hejlsberg ผู้สร้าง C # กล่าวว่าหนึ่งในข้อผิดพลาดที่ใหญ่ที่สุดใน C # นั้นไม่ได้รวมประเภทการอ้างอิงที่ไม่สามารถยกเลิกได้ มันเป็นหนึ่งในเหตุผลหลักที่ทำให้รหัสยามจำเป็นมาก

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

เห็นด้วยกับ @duros ในส่วนที่เหลือของมัน


0

ทำทั้งสองอย่าง แต่ทำวิธีการช่วยเหลือแบบคงที่เพื่อชี้แจงความตั้งใจของคุณ นี่คือสิ่งที่ Google ทำเพื่อ Java ตรวจสอบ code.google.com/p/guava-l ไลบรารี/wiki/PreconditionsExplained

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