บูรณาการอย่างต่อเนื่องสำหรับซอฟต์แวร์ทางวิทยาศาสตร์


22

ฉันไม่ใช่วิศวกรซอฟต์แวร์ ฉันเป็นนักเรียนปริญญาเอกในสาขาธรณีศาสตร์

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

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

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

ฉันมีคำถามสองสามข้อที่ฉันต้องการคำแนะนำ:

ก่อนอื่นคำอธิบายสั้น ๆ เกี่ยวกับการทำงานของซอฟต์แวร์:

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

  • มันเป็นซอฟต์แวร์ทางวิทยาศาสตร์ ฟังก์ชั่นเกือบทั้งหมดมีพารามิเตอร์อินพุตหลายค่าซึ่งชนิดส่วนใหญ่เป็นคลาสที่ค่อนข้างซับซ้อน ฉันมีไฟล์. txt หลายไฟล์ที่มีแค็ตตาล็อกขนาดใหญ่ซึ่งใช้สร้างอินสแตนซ์ของคลาสเหล่านี้

ตอนนี้มาคำถามของฉัน:

  1. การทดสอบหน่วยการทดสอบบูรณาการการทดสอบแบบครบวงจร? : ซอฟต์แวร์ของฉันมีโค้ดประมาณ 30,000 บรรทัดพร้อมฟังก์ชั่นนับร้อยและ ~ 80 คลาส ฉันรู้สึกแปลก ๆ ที่เริ่มเขียนการทดสอบหน่วยสำหรับฟังก์ชั่นนับร้อยที่นำไปใช้แล้ว ดังนั้นฉันจึงคิดถึงการสร้างกรณีทดสอบ เตรียมไฟล์. xml ที่แตกต่างกัน 10-20 ไฟล์แล้วปล่อยให้ซอฟต์แวร์ทำงาน ฉันเดาว่านี่คือสิ่งที่เรียกว่าการทดสอบแบบครบวงจร? ฉันมักจะอ่านว่าคุณไม่ควรทำสิ่งนี้ แต่อาจเป็นการเริ่มต้นถ้าคุณมีซอฟต์แวร์ที่ใช้งานได้หรือไม่ หรือเป็นเพียงความคิดโง่ ๆ ที่พยายามเพิ่ม CI ให้กับซอฟต์แวร์ที่ใช้งานได้

  2. คุณจะเขียนการทดสอบหน่วยได้อย่างไรถ้าพารามิเตอร์ฟังก์ชั่นยากที่จะสร้าง? ถือว่าผมมีฟังก์ชั่นdouble fun(vector<Class_A> a, vector<Class_B>)และมักจะผมจะต้องอ่านครั้งแรกในไฟล์ข้อความต่างๆเพื่อสร้างวัตถุชนิดและClass_A Class_Bฉันคิดเกี่ยวกับการสร้างฟังก์ชั่นจำลองบางอย่างClass_A create_dummy_object()โดยไม่ต้องอ่านในไฟล์ข้อความ ผมยังคิดว่าเกี่ยวกับการใช้ชนิดของอนุกรม (ฉันไม่ได้วางแผนที่จะทดสอบการสร้างคลาสอ็อบเจ็กต์เนื่องจากขึ้นอยู่กับไฟล์ข้อความหลายไฟล์เท่านั้น)

  3. จะเขียนแบบทดสอบได้อย่างไรถ้าผลลัพธ์มีความผันแปรสูง ซอฟต์แวร์ของฉันใช้การจำลอง monte-carlo ขนาดใหญ่และทำงานซ้ำ ๆ โดยปกติคุณจะมีการทำซ้ำประมาณ 1,000 ครั้งและทุกๆครั้งที่ทำซ้ำคุณกำลังสร้างอินสแตนซ์ของวัตถุประมาณ 500-20,000 รายการตามการจำลองของมอนเตคาร์โล หากหนึ่งผลลัพธ์ของการทำซ้ำหนึ่งครั้งจะแตกต่างกันเล็กน้อยการทำซ้ำที่กำลังจะเกิดขึ้นทั้งหมดนั้นจะแตกต่างกันโดยสิ้นเชิง คุณจัดการกับสถานการณ์นี้อย่างไร ฉันเดาว่านี่เป็นประเด็นใหญ่สำหรับการทดสอบแบบ end-to-end เนื่องจากผลลัพธ์ที่ได้คือตัวแปรที่สูงมาก?

คำแนะนำอื่น ๆ ของ CI นั้นได้รับการชื่นชมอย่างมาก



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

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

คำตอบ:


23

การทดสอบซอฟต์แวร์ทางวิทยาศาสตร์เป็นเรื่องยากทั้งเนื่องจากเรื่องที่ซับซ้อนและเนื่องจากกระบวนการพัฒนาทางวิทยาศาสตร์ทั่วไป (หรือที่รู้จักว่าแฮ็คมันจนกว่าจะใช้งานได้ นี่เป็นเรื่องน่าขันเล็กน้อยเมื่อพิจารณาว่าวิทยาศาสตร์ควรทำซ้ำได้ การเปลี่ยนแปลงใดที่เปรียบเทียบกับซอฟต์แวร์“ ปกติ” ไม่ใช่การทดสอบนั้นมีประโยชน์ (ใช่!) แต่การทดสอบประเภทใดที่เหมาะสม

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

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

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

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

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

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

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

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


7

ฉันรู้สึกแปลก ๆ ที่เริ่มเขียนการทดสอบหน่วยสำหรับฟังก์ชั่นนับร้อยที่นำไปใช้แล้ว

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

คุณจะเขียนการทดสอบหน่วยได้อย่างไรถ้าพารามิเตอร์ฟังก์ชั่นยากที่จะสร้าง?

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

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

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

จะเขียนแบบทดสอบได้อย่างไรถ้าผลลัพธ์มีความผันแปรสูง

เคล็ดลับสองสามข้อ:

1) ใช้ผู้ผกผัน (หรือมากกว่านั้นโดยทั่วไปคือการทดสอบตามคุณสมบัติ) fft ของ[1,2,3,4,5]อะไร ไม่มีความเห็น. อะไรนะifft(fft([1,2,3,4,5]))? ควรจะเป็น[1,2,3,4,5](หรือใกล้กับข้อผิดพลาดจุดลอยตัวอาจเกิดขึ้น)

2) ใช้การยืนยัน "รู้จัก" ถ้าคุณเขียนฟังก์ชันดีเทอร์มิแนนต์มันอาจยากที่จะบอกว่าดีเทอร์มีแนนต์คือเมทริกซ์ 100x100 แต่คุณรู้ไหมว่าดีเทอร์มีแนนต์ของเมทริกซ์เอกลักษณ์คือ 1 แม้ว่าจะเป็น 100x100 คุณยังรู้ว่าฟังก์ชันควรส่งกลับค่า 0 บนเมทริกซ์ที่ไม่สามารถย้อนกลับได้ (เช่น 100x100 เต็มจาก 0 ทั้งหมด)

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

EXPECT_TRUE(reg(img1, img2).size() < min(img1.size(), img2.size()))

เนื่องจากคุณสามารถลงทะเบียนในส่วนที่ทับซ้อนกันเท่านั้นภาพที่ลงทะเบียนจะต้องมีขนาดเล็กกว่าหรือเท่ากับภาพที่เล็กที่สุดของคุณ) และด้วย:

scale = 255
EXPECT_PIXEL_EQ_WITH_TOLERANCE(reg(img, img), img, .05*scale)

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

4) ใช้หรือเก็บเมล็ดแบบสุ่มสำหรับ RNG ของคุณ

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

HTH


2
  1. ประเภทของการทดสอบ

    • ฉันรู้สึกแปลก ๆ ที่เริ่มเขียนการทดสอบหน่วยสำหรับฟังก์ชั่นนับร้อยที่นำไปใช้แล้ว

      ลองคิดอีกแง่หนึ่งว่า: หากแพทช์ที่สัมผัสกับฟังก์ชั่นหลาย ๆ ตัวแบ่งการทดสอบแบบ end-to-end ของคุณออกมาคุณจะคิดได้อย่างไรว่าอันไหนเป็นปัญหา

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

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

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

    • ใช่การเพิ่ม CI ให้กับซอฟต์แวร์ที่มีอยู่นั้นสมเหตุสมผลและปกติ

  2. วิธีเขียนการทดสอบหน่วย

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

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

  3. ผลลัพธ์ที่เปลี่ยนแปลงได้

    คุณต้องมีบางอย่างคงที่สำหรับผล ทดสอบสิ่งเหล่านั้นแทนที่จะเป็นค่าตัวเลขเดียว

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


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

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

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

  3. ปัญหาเกี่ยวกับหนังสือเกี่ยวกับเรื่องนี้คือภูมิทัศน์ของ CI (และส่วนอื่น ๆ ของผู้ devops) วิวัฒนาการอย่างรวดเร็วทุกอย่างในหนังสือน่าจะหมดไปในอีกไม่กี่เดือนต่อมา ฉันไม่รู้หนังสือที่สามารถช่วยคุณได้ แต่ Google ควรเป็นผู้ช่วยให้รอดของคุณเช่นเคย

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

เคล็ดลับบางอย่าง:

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

1

ในการตอบกลับก่อนamonได้กล่าวถึงจุดสำคัญบางอย่างแล้ว ให้ฉันเพิ่มอีก:

1. ความแตกต่างระหว่างการพัฒนาซอฟต์แวร์เชิงวิทยาศาสตร์และซอฟต์แวร์เชิงพาณิชย์

สำหรับซอฟแวร์ทางวิทยาศาสตร์โดยปกติแล้วการโฟกัสจะอยู่ที่ปัญหาทางวิทยาศาสตร์แน่นอน ปัญหาคือการจัดการพื้นหลังทางทฤษฎีมากขึ้นการหาวิธีการเชิงตัวเลขที่ดีที่สุด ฯลฯ ซอฟต์แวร์เป็นเพียงส่วนเล็ก ๆ น้อย ๆ หรือน้อยของงาน

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

โดยทั่วไปซอฟต์แวร์เชิงพาณิชย์ได้รับการพัฒนาโดยทีมงานขนาดใหญ่ในระยะเวลานาน สิ่งนี้ต้องใช้การวางแผนมากมายสำหรับสถาปัตยกรรมการออกแบบการทดสอบหน่วยการทดสอบการรวม ฯลฯ การวางแผนนี้ต้องใช้เวลาและประสบการณ์จำนวนน้อยมาก ในสภาพแวดล้อมทางวิทยาศาสตร์นั้นปกติจะไม่มีเวลา

หากคุณต้องการแปลงโครงการของคุณเป็นซอฟต์แวร์ที่คล้ายกับซอฟต์แวร์เชิงพาณิชย์คุณควรตรวจสอบสิ่งต่อไปนี้:

  • คุณมีเวลาและทรัพยากรไหม?
  • มุมมองระยะยาวของซอฟต์แวร์คืออะไร? จะเกิดอะไรขึ้นกับซอฟต์แวร์เมื่อคุณทำงานเสร็จและออกจากมหาวิทยาลัย?

2. จบการทดสอบ

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

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

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

ตัวอย่างควรครอบคลุมผลลัพธ์ทั่วไปของซอฟต์แวร์ของคุณ ซึ่งควรรวมถึงกรณีขอบของพื้นที่พารามิเตอร์และอัลกอริธึมเชิงตัวเลขของคุณ

คุณควรพยายามหาตัวอย่างที่ไม่ต้องการเวลามากเกินไปในการทำงาน แต่ยังครอบคลุมถึงกรณีทดสอบทั่วไป

3. บูรณาการอย่างต่อเนื่อง

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

ดังนั้นฉันจึงคิดว่าการบูรณาการในวิธีที่ดีกว่าควรพูดคุยเกี่ยวกับภูมิหลังทางทฤษฎีและวิธีการเชิงตัวเลขการทดสอบอย่างระมัดระวัง ฯลฯ

ฉันไม่คิดว่ามันเป็นความคิดที่ดีที่จะมีระบบอัตโนมัติบางอย่างเพื่อบูรณาการอย่างต่อเนื่อง

อย่างไรก็ตามคุณใช้ระบบควบคุมเวอร์ชันหรือไม่?

4. ทดสอบอัลกอริธึมเชิงตัวเลขของคุณ

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

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

คุณสามารถทำการทดสอบเหล่านั้นในรหัสทดสอบของคุณและใช้อัลกอริทึมที่เร็วที่สุดสำหรับรหัสการผลิตของคุณ


0

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

ที่กล่าวว่าสิ่งที่มีค่าคือ:

  • มันเป็นวิธีที่ดีที่สุดในเวลาในแง่ของอัลกอริทึม?
  • ความง่ายในการพอร์ตไปยังแพลตฟอร์มการคำนวณที่แตกต่างกัน (สภาพแวดล้อม HPC ที่แตกต่างกัน, รสชาติของระบบปฏิบัติการ ฯลฯ )
  • ความทนทาน - มันรันบนชุดข้อมูลของฉันหรือไม่

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

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