8 แนวทางปฏิบัติที่ดีที่สุดในการทดสอบอัตโนมัติเพื่อประสบการณ์การทดสอบเชิงบวก
เผยแพร่แล้ว: 2022-03-11ไม่น่าแปลกใจที่นักพัฒนาหลายคนมองว่าการทดสอบเป็นสิ่งชั่วร้ายที่จำเป็นซึ่งทำลายเวลาและพลังงาน: การทดสอบอาจน่าเบื่อ ไม่เกิดผล และซับซ้อนโดยสิ้นเชิง
ประสบการณ์ครั้งแรกของฉันกับการทดสอบแย่มาก ฉันทำงานในทีมที่มีข้อกำหนดด้านรหัสที่เข้มงวด เวิร์กโฟลว์คือ: ใช้คุณลักษณะ ดีบัก และเขียนการทดสอบเพื่อให้แน่ใจว่าโค้ดครอบคลุมทั้งหมด ทีมไม่มีการทดสอบการรวม มีเพียงการทดสอบหน่วยที่มีการจำลองเริ่มต้นด้วยตนเองจำนวนมาก และการทดสอบหน่วยส่วนใหญ่ทดสอบการแมปด้วยตนเองเล็กน้อยในขณะที่ใช้ไลบรารีเพื่อทำแผนที่อัตโนมัติ ทุกการทดสอบพยายามยืนยันคุณสมบัติทั้งหมดที่มี ดังนั้นการเปลี่ยนแปลงทุกครั้งจึงทำลายการทดสอบหลายสิบครั้ง
ฉันไม่ชอบทำงานกับการทดสอบเพราะถูกมองว่าเป็นภาระที่ต้องใช้เวลามาก อย่างไรก็ตาม ฉันไม่ยอมแพ้ การทดสอบความมั่นใจให้และการตรวจสอบอัตโนมัติหลังจากการเปลี่ยนแปลงเล็ก ๆ น้อย ๆ ทำให้ฉันสนใจ ฉันเริ่มอ่านและฝึกฝน และเรียนรู้ว่าการทดสอบเมื่อทำถูกต้องแล้ว จะเป็นประโยชน์ และ น่าสนุก
ในบทความนี้ ฉันแบ่งปันแนวทางปฏิบัติที่ดีที่สุดสำหรับการทดสอบอัตโนมัติ 8 ประการที่ฉันหวังว่าฉันจะรู้ตั้งแต่แรก
ทำไมคุณถึงต้องการกลยุทธ์การทดสอบอัตโนมัติ
การทดสอบอัตโนมัติมักมุ่งเน้นไปที่อนาคต แต่เมื่อคุณนำไปใช้อย่างถูกต้อง คุณจะได้รับประโยชน์ทันที การใช้เครื่องมือที่ช่วยให้คุณทำงานได้ดีขึ้นสามารถประหยัดเวลาและทำให้งานของคุณสนุกยิ่งขึ้น
ลองนึกภาพว่าคุณกำลังพัฒนาระบบที่ดึงใบสั่งซื้อจาก ERP ของบริษัท และส่งคำสั่งซื้อเหล่านั้นกับผู้ขาย คุณมีราคาสินค้าที่สั่งซื้อก่อนหน้านี้ใน ERP แต่ราคาปัจจุบันอาจแตกต่างกัน คุณต้องการควบคุมว่าจะสั่งซื้อในราคาที่ต่ำกว่าหรือสูงกว่า คุณมีการตั้งค่าผู้ใช้ที่จัดเก็บไว้ และคุณกำลังเขียนโค้ดเพื่อจัดการกับความผันผวนของราคา
คุณจะตรวจสอบได้อย่างไรว่าโค้ดทำงานตามที่คาดไว้ คุณอาจจะ:
- สร้างคำสั่งซื้อจำลองในอินสแตนซ์ของ ERP ของนักพัฒนาซอฟต์แวร์ (สมมติว่าคุณตั้งค่าไว้ล่วงหน้า)
- เรียกใช้แอปของคุณ
- เลือกคำสั่งซื้อนั้นและเริ่มกระบวนการวางคำสั่งซื้อ
- รวบรวมข้อมูลจากฐานข้อมูลของ ERP
- ขอราคาปัจจุบันจาก API ของผู้ขาย
- แทนที่ราคาในโค้ดเพื่อสร้างเงื่อนไขเฉพาะ
คุณหยุดที่จุดพักและสามารถไปทีละขั้นตอนเพื่อดูว่าจะเกิดอะไรขึ้นสำหรับสถานการณ์หนึ่ง แต่มีหลายสถานการณ์ที่เป็นไปได้:
| การตั้งค่า | ราคา ERP | ราคาจำหน่าย | เราควรสั่งซื้อ? | |
|---|---|---|---|---|
| ยอมให้ราคาสูงขึ้น | ขออนุญาติลดราคา | |||
| เท็จ | เท็จ | 10 | 10 | จริง |
| (ในที่นี้จะมีชุดค่าผสมการตั้งค่าเพิ่มเติมสามชุด แต่ราคาเท่ากัน ดังนั้นผลลัพธ์จึงเหมือนกัน) | ||||
| จริง | เท็จ | 10 | 11 | จริง |
| จริง | เท็จ | 10 | 9 | เท็จ |
| เท็จ | จริง | 10 | 11 | เท็จ |
| เท็จ | จริง | 10 | 9 | จริง |
| จริง | จริง | 10 | 11 | จริง |
| จริง | จริง | 10 | 9 | จริง |
ในกรณีที่เกิดข้อผิดพลาด บริษัทอาจสูญเสียเงิน สร้างความเสียหายต่อชื่อเสียงของบริษัท หรือทั้งสองอย่าง คุณต้องตรวจสอบหลาย ๆ สถานการณ์และวนรอบการตรวจสอบซ้ำหลายครั้ง การทำเช่นนี้ด้วยตนเองจะน่าเบื่อ แต่การทดสอบอยู่ที่นี่เพื่อช่วย!
การทดสอบช่วยให้คุณสร้างบริบทโดยไม่ต้องเรียกใช้ API ที่ไม่เสถียร พวกเขาขจัดความจำเป็นในการคลิกซ้ำ ๆ ผ่านอินเทอร์เฟซเก่าและช้าซึ่งมีอยู่ทั่วไปในระบบ ERP แบบเดิม สิ่งที่คุณต้องทำคือกำหนดบริบทสำหรับยูนิตหรือระบบย่อย จากนั้นการดีบัก การแก้ไขปัญหา หรือการสำรวจสถานการณ์จะเกิดขึ้นทันที คุณทำการทดสอบและกลับสู่โค้ดของคุณ ความชอบของฉันคือการตั้งค่าการโยงคีย์ใน IDE ที่ทำซ้ำการทดสอบครั้งก่อนของฉัน โดยให้ข้อเสนอแนะอัตโนมัติทันทีที่ฉันทำการเปลี่ยนแปลง
1. รักษาทัศนคติที่ถูกต้อง
เมื่อเทียบกับการดีบักด้วยตนเองและการทดสอบตัวเอง การทดสอบอัตโนมัติจะมีประสิทธิภาพมากกว่าตั้งแต่เริ่มต้น แม้กระทั่งก่อนที่จะคอมมิตโค้ดการทดสอบใดๆ หลังจากที่คุณตรวจสอบว่าโค้ดของคุณทำงานตามที่คาดไว้—โดยการทดสอบด้วยตนเองหรือบางทีสำหรับโมดูลที่ซับซ้อนมากขึ้น โดยการก้าวผ่านมันด้วยดีบักเกอร์ระหว่างการทดสอบ— คุณสามารถใช้การยืนยันเพื่อกำหนดสิ่งที่คุณคาดหวังสำหรับพารามิเตอร์อินพุตชุดค่าผสมใดๆ
เมื่อการทดสอบผ่าน คุณก็เกือบจะพร้อมที่จะคอมมิต แต่ก็ไม่มากพอ เตรียมปรับโครงสร้างโค้ดของคุณใหม่ เนื่องจากเวอร์ชันแรกที่ใช้งานได้มักจะไม่สวยงาม คุณจะทำการ refactoring นั้นโดยไม่มีการทดสอบหรือไม่? เป็นเรื่องที่น่าสงสัยเพราะคุณต้องทำตามขั้นตอนด้วยตนเองทั้งหมดอีกครั้ง ซึ่งอาจทำให้ความกระตือรือร้นของคุณลดลง
แล้วอนาคตล่ะ? ขณะดำเนินการปรับโครงสร้างใหม่ เพิ่มประสิทธิภาพ หรือเพิ่มคุณสมบัติ การทดสอบช่วยให้มั่นใจว่าโมดูลจะยังคงทำงานตามที่คาดไว้หลังจากที่คุณทำการเปลี่ยนแปลง ซึ่งจะทำให้เกิดความมั่นใจที่ยั่งยืนและช่วยให้นักพัฒนารู้สึกพร้อมที่จะรับมือกับงานที่จะเกิดขึ้น
การคิดว่าการทดสอบเป็นภาระหรือสิ่งที่ทำให้ผู้ตรวจสอบโค้ดเท่านั้นหรือผู้นำมีความสุขนั้นไม่เป็นผล การทดสอบเป็นเครื่องมือที่เราในฐานะนักพัฒนาได้รับประโยชน์ เราชอบเวลาที่โค้ดของเราทำงาน และเราไม่ชอบที่จะใช้เวลากับการกระทำซ้ำๆ หรือแก้ไขโค้ดเพื่อแก้ไขจุดบกพร่อง
เมื่อเร็ว ๆ นี้ ฉันทำงานเกี่ยวกับการปรับโครงสร้างใหม่ใน codebase ของฉันและขอให้ IDE ของฉันล้างข้อมูลที่ไม่ได้ใช้งาน using คำสั่ง ฉันประหลาดใจมาก การทดสอบพบว่าระบบการรายงานอีเมลของฉันมีข้อผิดพลาดหลายประการ อย่างไรก็ตาม เป็นความล้มเหลวที่ถูกต้อง กระบวนการล้างข้อมูลได้ลบบางส่วน using คำสั่งในโค้ด Razor (HTML + C#) ของฉันสำหรับเทมเพลตอีเมล และเครื่องมือเทมเพลตก็ไม่สามารถสร้าง HTML ที่ถูกต้องได้เป็นผล ฉันไม่ได้คาดหวังว่าการดำเนินการเล็กน้อยดังกล่าวจะทำให้การรายงานทางอีเมลเสียหาย การทดสอบช่วยให้ฉันไม่ต้องเสียเวลาหลายชั่วโมงในการจับจุดบกพร่องทั่วทั้งแอปก่อนเปิดตัว เมื่อฉันคิดว่าทุกอย่างจะได้ผล
แน่นอน คุณต้องรู้วิธีใช้เครื่องมือและอย่ากรีดนิ้วที่เลื่องลือ อาจดูเหมือนว่าการกำหนดบริบทเป็นเรื่องที่น่าเบื่อหน่ายและอาจยากกว่าการเรียกใช้แอป ซึ่งการทดสอบต้องการการบำรุงรักษามากเกินไปเพื่อหลีกเลี่ยงการไม่อัปเดตและไร้ประโยชน์ นี่เป็นคะแนนที่ถูกต้องและเราจะแก้ไข
2. เลือกประเภทการทดสอบที่เหมาะสม
นักพัฒนามักจะไม่ชอบการทดสอบอัตโนมัติเพราะพวกเขาพยายามเยาะเย้ยการขึ้นต่อกันหลายสิบครั้งเพียงเพื่อตรวจสอบว่าโค้ดถูกเรียกหรือไม่ อีกทางหนึ่ง นักพัฒนาต้องเผชิญกับการทดสอบระดับสูงและพยายามทำซ้ำทุกสถานะแอปพลิเคชันเพื่อตรวจสอบการเปลี่ยนแปลงทั้งหมดในโมดูลขนาดเล็ก รูปแบบเหล่านี้ไม่ได้ผลและน่าเบื่อ แต่เราสามารถหลีกเลี่ยงได้โดยใช้การทดสอบประเภทต่างๆ ตามที่ตั้งใจไว้ (การทดสอบควรใช้งานได้จริงและน่าสนุก!)
ผู้อ่านจะต้องรู้ว่าการทดสอบหน่วยคืออะไรและจะเขียนอย่างไร และทำความคุ้นเคยกับการทดสอบการรวม—ถ้าไม่ใช่ ก็ควรหยุดที่นี่เพื่อเร่งความเร็ว
มีประเภทการทดสอบหลายสิบประเภท แต่ประเภททั่วไป 5 ประเภทนี้เป็นชุดค่าผสมที่มีประสิทธิภาพสูงสุด:
- การทดสอบหน่วย ใช้เพื่อทดสอบโมดูลแบบแยกโดยเรียกใช้เมธอดโดยตรง การพึ่งพาอาศัยกันไม่ได้ถูกทดสอบ ดังนั้นจึงเป็นการเยาะเย้ย
- การทดสอบการรวม ใช้เพื่อทดสอบระบบย่อย คุณยังคงใช้การเรียกโดยตรงไปยังเมธอดของโมดูลเอง แต่ที่นี่เราให้ความสำคัญกับการพึ่งพา ดังนั้นอย่าใช้การขึ้นต่อกันที่จำลองขึ้น—เฉพาะโมดูลที่ขึ้นกับของจริงเท่านั้น (การผลิต) คุณยังสามารถใช้ฐานข้อมูลในหน่วยความจำหรือเว็บเซิร์ฟเวอร์ที่จำลองได้ เนื่องจากสิ่งเหล่านี้เป็นการจำลองโครงสร้างพื้นฐาน
- การทดสอบฟังก์ชัน คือการทดสอบสำหรับแอปพลิเคชันทั้งหมด หรือที่เรียกว่าการทดสอบแบบ end-to-end (E2E) คุณใช้ไม่มีสายตรง แต่การโต้ตอบทั้งหมดต้องผ่าน API หรืออินเทอร์เฟซผู้ใช้—นี่คือการทดสอบจากมุมมองของผู้ใช้ปลายทาง อย่างไรก็ตาม โครงสร้างพื้นฐานยังคงถูกล้อเลียน
- การทดสอบ Canary นั้นคล้ายกับการทดสอบการใช้งาน แต่มีโครงสร้างพื้นฐานด้านการผลิตและชุดการดำเนินการที่เล็กกว่า ใช้เพื่อให้แน่ใจว่าแอปพลิเคชันที่ปรับใช้ใหม่ทำงาน
- การทดสอบโหลด คล้ายกับการทดสอบ Canary แต่มีโครงสร้างพื้นฐานสำหรับแสดงจริงและชุดการดำเนินการที่เล็กกว่า ซึ่งทำซ้ำหลายครั้ง
ไม่จำเป็นเสมอไปที่จะต้องทำงานกับการทดสอบทั้งห้าประเภทตั้งแต่เริ่มต้น ในกรณีส่วนใหญ่ คุณสามารถไปได้ไกลด้วยการทดสอบสามครั้งแรก
เราจะตรวจสอบกรณีการใช้งานแต่ละประเภทโดยสังเขปเพื่อช่วยคุณเลือกกรณีที่เหมาะสมกับความต้องการของคุณ
การทดสอบหน่วย
จำตัวอย่างด้วยราคาที่แตกต่างกันและการตั้งค่าการจัดการ เป็นตัวเลือกที่ดีสำหรับการทดสอบหน่วย เพราะเราสนใจเฉพาะสิ่งที่เกิดขึ้นภายในโมดูล และผลลัพธ์ก็มีการแยกสาขาทางธุรกิจที่สำคัญ
โมดูลมีพารามิเตอร์อินพุตที่แตกต่างกันจำนวนมาก และเราต้องการรับค่าส่งคืนที่ถูกต้องสำหรับอาร์กิวเมนต์ที่ถูกต้องทุกชุด การทดสอบหน่วยช่วยให้มั่นใจได้ถึงความถูกต้อง เนื่องจากช่วยให้เข้าถึงพารามิเตอร์อินพุตของฟังก์ชันหรือเมธอดได้โดยตรง และคุณไม่จำเป็นต้องเขียนวิธีทดสอบหลายสิบวิธีเพื่อให้ครอบคลุมทุกชุดค่าผสม ในหลายภาษา คุณสามารถหลีกเลี่ยงวิธีทดสอบที่ซ้ำกันได้โดยการกำหนดวิธีการ ซึ่งยอมรับอาร์กิวเมนต์ที่จำเป็นสำหรับโค้ดของคุณและผลลัพธ์ที่คาดหวัง จากนั้น คุณสามารถใช้เครื่องมือทดสอบเพื่อจัดเตรียมชุดค่าต่างๆ และความคาดหวังสำหรับวิธีกำหนดพารามิเตอร์นั้น
การทดสอบบูรณาการ
การทดสอบการผสานรวมเหมาะอย่างยิ่งสำหรับกรณีต่างๆ เมื่อคุณสนใจว่าโมดูลโต้ตอบกับการพึ่งพา โมดูลอื่นๆ หรือโครงสร้างพื้นฐานอย่างไร คุณยังคงใช้การเรียกเมธอดโดยตรง แต่ไม่มีการเข้าถึงโมดูลย่อย ดังนั้นการพยายามทดสอบสถานการณ์ทั้งหมดสำหรับวิธีการป้อนข้อมูลทั้งหมดของโมดูลย่อยทั้งหมดจึงไม่สามารถทำได้
โดยทั่วไปแล้ว ฉันชอบที่จะมีสถานการณ์ความสำเร็จหนึ่งสถานการณ์และสถานการณ์ความล้มเหลวหนึ่งสถานการณ์ต่อโมดูล
ฉันชอบที่จะใช้การทดสอบการรวมเพื่อตรวจสอบว่าสร้างคอนเทนเนอร์การพึ่งพาอาศัยได้สำเร็จหรือไม่ ไม่ว่าไปป์ไลน์การประมวลผลหรือการคำนวณจะส่งคืนผลลัพธ์ที่คาดไว้ หรือข้อมูลที่ซับซ้อนถูกอ่านและแปลงอย่างถูกต้องจากฐานข้อมูลหรือ API ของบุคคลที่สามหรือไม่
การทดสอบการทำงานหรือ E2E
การทดสอบเหล่านี้ทำให้คุณมั่นใจมากที่สุดว่าแอปของคุณใช้งานได้ เนื่องจากเป็นการตรวจสอบว่าแอปของคุณสามารถเริ่มทำงานได้โดยไม่มีข้อผิดพลาดรันไทม์เป็นอย่างน้อย การเริ่มต้นทดสอบโค้ดของคุณโดยไม่ต้องเข้าถึงคลาสของโค้ดนั้นใช้เวลาทำงานมากกว่าเดิม แต่เมื่อคุณเข้าใจและเขียนการทดสอบสองสามครั้งแรกแล้ว คุณจะพบว่ามันไม่ยากเกินไป
เรียกใช้แอปพลิเคชันโดยเริ่มกระบวนการด้วยอาร์กิวเมนต์บรรทัดคำสั่ง หากจำเป็น จากนั้นใช้แอปพลิเคชันตามที่ผู้มีโอกาสเป็นลูกค้าทำ: โดยการเรียกจุดปลาย API หรือกดปุ่ม ไม่ยากแม้แต่ในกรณีของการทดสอบ UI: แพลตฟอร์มหลักแต่ละแพลตฟอร์มมีเครื่องมือในการค้นหาองค์ประกอบภาพใน UI
การทดสอบนกขมิ้น
การทดสอบการทำงานจะแจ้งให้คุณทราบว่าแอปของคุณทำงานในสภาพแวดล้อมการทดสอบหรือไม่ แต่สภาพแวดล้อมที่ใช้งานจริงเป็นอย่างไร สมมติว่าคุณกำลังทำงานกับ API ของบุคคลที่สามหลายตัว และคุณต้องการมีแดชบอร์ดของสถานะของพวกเขา หรือต้องการดูว่าแอปพลิเคชันของคุณจัดการกับคำขอที่เข้ามาอย่างไร นี่เป็นกรณีการใช้งานทั่วไปสำหรับการทดสอบนกขมิ้น
พวกเขาทำงานโดยดำเนินการสั้น ๆ บนระบบการทำงานโดยไม่ก่อให้เกิดผลข้างเคียงต่อระบบของบุคคลที่สาม ตัวอย่างเช่น คุณสามารถลงทะเบียนผู้ใช้ใหม่หรือตรวจสอบความพร้อมของผลิตภัณฑ์โดยไม่ต้องทำการสั่งซื้อ
จุดประสงค์ของการทดสอบ Canary คือเพื่อให้แน่ใจว่าส่วนประกอบหลักทั้งหมดทำงานร่วมกันในสภาพแวดล้อมที่ใช้งานจริง จะไม่ล้มเหลวเนื่องจากปัญหาด้านข้อมูลประจำตัว
โหลดการทดสอบ
การทดสอบโหลดเปิดเผยว่าแอปพลิเคชันของคุณจะทำงานต่อเมื่อมีผู้คนจำนวนมากเริ่มใช้งานหรือไม่ คล้ายกับการทดสอบ canary และ functional แต่ไม่ได้ดำเนินการในสภาพแวดล้อมจริงหรือในสภาพแวดล้อมที่ใช้งานจริง โดยปกติ สภาวะแวดล้อมการจัดเตรียมพิเศษจะถูกใช้ ซึ่งคล้ายกับสภาวะแวดล้อมที่ใช้งานจริง
สิ่งสำคัญคือต้องทราบว่าการทดสอบเหล่านี้ไม่ได้ใช้บริการของบุคคลที่สามจริง ซึ่งอาจไม่พอใจกับการทดสอบโหลดภายนอกของบริการที่ใช้งานจริงและอาจมีค่าใช้จ่ายเพิ่มเติม
3. แยกประเภทการทดสอบออก
ในการจัดทำแผนการทดสอบอัตโนมัติของคุณ การทดสอบแต่ละประเภทควรแยกกันเพื่อให้สามารถดำเนินการได้อย่างอิสระ แม้ว่าสิ่งนี้จะต้องมีการจัดระเบียบเพิ่มเติม แต่ก็คุ้มค่าเพราะการทดสอบแบบผสมสามารถสร้างปัญหาได้
การทดสอบเหล่านี้มีความแตกต่างกัน:
- ความตั้งใจและแนวคิดพื้นฐาน (การแยกจากกันจะเป็นแบบอย่างที่ดีสำหรับคนต่อไปที่มองโค้ด ซึ่งรวมถึง "อนาคตของคุณ")
- เวลาดำเนินการ (ดังนั้นการรันการทดสอบหน่วยก่อนจะช่วยให้รอบการทดสอบเร็วขึ้นเมื่อการทดสอบล้มเหลว)
- การพึ่งพา (ดังนั้นจึงมีประสิทธิภาพมากกว่าที่จะโหลดเฉพาะสิ่งที่จำเป็นภายในประเภทการทดสอบ)
- โครงสร้างพื้นฐานที่จำเป็น
- ภาษาโปรแกรม (ในบางกรณี)
- ตำแหน่งในไปป์ไลน์แบบต่อเนื่อง (CI) หรือภายนอก
สิ่งสำคัญที่ควรทราบคือ ภาษาและกลุ่มเทคโนโลยีส่วนใหญ่ คุณสามารถจัดกลุ่มได้ ตัวอย่างเช่น การทดสอบหน่วยทั้งหมดร่วมกับโฟลเดอร์ย่อยที่ตั้งชื่อตามโมดูลการทำงาน ซึ่งสะดวก ลดแรงเสียดทานเมื่อสร้างโมดูลการทำงานใหม่ ง่ายขึ้นสำหรับการสร้างแบบอัตโนมัติ ส่งผลให้เกิดความยุ่งเหยิงน้อยลง และเป็นอีกวิธีหนึ่งในการทำให้การทดสอบง่ายขึ้น
4. เรียกใช้การทดสอบของคุณโดยอัตโนมัติ
ลองนึกภาพสถานการณ์ที่คุณเขียนการทดสอบบางอย่าง แต่หลังจากดึง repo ของคุณในอีกไม่กี่สัปดาห์ต่อมา คุณสังเกตเห็นว่าการทดสอบเหล่านั้นไม่ผ่านอีกต่อไป
นี่เป็นเครื่องเตือนใจที่ไม่น่าพอใจว่าการทดสอบเป็นโค้ด และต้องรักษาไว้เช่นเดียวกับโค้ดอื่นๆ เวลาที่ดีที่สุดสำหรับสิ่งนี้คือก่อนช่วงเวลาที่คุณคิดว่าคุณทำงานเสร็จแล้ว และต้องการดูว่าทุกอย่างยังทำงานตามที่ตั้งใจไว้หรือไม่ คุณมีบริบททั้งหมดที่จำเป็น และคุณสามารถแก้ไขโค้ดหรือเปลี่ยนการทดสอบที่ล้มเหลวได้ง่ายกว่าเพื่อนร่วมงานที่ทำงานบนระบบย่อยอื่น แต่ช่วงเวลานี้มีอยู่ในใจของคุณเท่านั้น ดังนั้นวิธีทั่วไปที่สุดในการทดสอบคือโดยอัตโนมัติหลังจากการพุชไปยังสาขาการพัฒนาหรือหลังจากสร้างคำขอดึง

ด้วยวิธีนี้ สาขาหลักของคุณจะอยู่ในสถานะที่ถูกต้องเสมอ หรืออย่างน้อย คุณก็จะมีตัวบ่งชี้ที่ชัดเจนของสถานะของสาขานั้น ไปป์ไลน์การสร้างและทดสอบอัตโนมัติ—หรือไปป์ไลน์ CI—ช่วย:
- ตรวจสอบให้แน่ใจว่าโค้ดสามารถสร้างได้
- ขจัดปัญหาที่อาจเกิดขึ้น "ใช้งานได้กับเครื่องของฉัน"
- ให้คำแนะนำที่รันได้เกี่ยวกับวิธีการเตรียมสภาพแวดล้อมการพัฒนา
การกำหนดค่าไปป์ไลน์นี้ต้องใช้เวลา แต่ไปป์ไลน์สามารถเปิดเผยปัญหาต่างๆ ได้ก่อนที่จะเข้าถึงผู้ใช้หรือไคลเอนต์ แม้ว่าคุณจะเป็นนักพัฒนาเพียงคนเดียวก็ตาม
เมื่อดำเนินการแล้ว CI ยังเปิดเผยปัญหาใหม่ก่อนที่จะมีโอกาสเติบโตในขอบเขต ดังนั้น ฉันชอบที่จะตั้งค่าทันทีหลังจากเขียนการทดสอบครั้งแรก คุณสามารถโฮสต์โค้ดของคุณในที่เก็บส่วนตัวบน GitHub และตั้งค่า GitHub Actions หาก repo ของคุณเป็นแบบสาธารณะ คุณมีตัวเลือกมากกว่า GitHub Actions ตัวอย่างเช่น แผนการทดสอบอัตโนมัติของฉันทำงานบน AppVeyor สำหรับโครงการที่มีฐานข้อมูลและการทดสอบสามประเภท
ฉันชอบที่จะจัดโครงสร้างไปป์ไลน์สำหรับโครงการการผลิตดังนี้:
- การรวบรวมหรือการถ่ายเท
- การทดสอบหน่วย: รวดเร็วและไม่ต้องการการพึ่งพา
- การตั้งค่าและการเริ่มต้นของฐานข้อมูลหรือบริการอื่นๆ
- การทดสอบการรวม: มีการพึ่งพานอกโค้ดของคุณ แต่เร็วกว่าการทดสอบการใช้งาน
- การทดสอบการทำงาน: เมื่อขั้นตอนอื่นๆ เสร็จสิ้น ให้รันทั้งแอป
ไม่มีการทดสอบนกขมิ้นหรือการทดสอบการโหลด เนื่องจากมีลักษณะเฉพาะและข้อกำหนด จึงควรเริ่มต้นด้วยตนเอง
5. เขียนเฉพาะการทดสอบที่จำเป็น
การเขียนการทดสอบหน่วยสำหรับโค้ดทั้งหมดเป็นกลยุทธ์ทั่วไป แต่บางครั้งอาจทำให้เสียเวลาและพลังงาน และไม่ทำให้คุณมั่นใจ หากคุณคุ้นเคยกับแนวคิด "การทดสอบปิรามิด" คุณอาจคิดว่าโค้ดทั้งหมดของคุณจะต้องครอบคลุมการทดสอบหน่วย โดยมีเพียงชุดย่อยที่ครอบคลุมโดยการทดสอบระดับสูงกว่าอื่นๆ
ฉันไม่เห็นความจำเป็นในการเขียนการทดสอบหน่วยเพื่อให้แน่ใจว่ามีการเรียกการอ้างอิงที่จำลองหลายครั้งในลำดับที่ต้องการ การทำเช่นนั้นต้องมีการตั้งค่าการเยาะเย้ยหลายครั้งและยืนยันการโทรทั้งหมด แต่ก็ยังไม่ทำให้ฉันมั่นใจว่าโมดูลนั้นใช้งานได้ โดยปกติ ฉันจะเขียนเฉพาะการทดสอบการรวมที่ใช้การพึ่งพาจริงและตรวจสอบเฉพาะผลลัพธ์เท่านั้น นั่นทำให้ฉันมั่นใจว่าไปป์ไลน์ในโมดูลที่ทดสอบทำงานอย่างถูกต้อง
โดยทั่วไปแล้ว ฉันเขียนการทดสอบที่ทำให้ชีวิตของฉันง่ายขึ้นในขณะที่ใช้ฟังก์ชันการทำงานและสนับสนุนในภายหลัง
สำหรับแอปพลิเคชันส่วนใหญ่ การมุ่งเป้าไปที่การครอบคลุมโค้ด 100% จะเพิ่มงานที่น่าเบื่อและขจัดความสุขจากการทำงานกับการทดสอบและการเขียนโปรแกรมโดยทั่วไป ตามที่ Martin Fowler's Test Coverage ระบุไว้:
ความครอบคลุมของการทดสอบเป็นเครื่องมือที่มีประโยชน์สำหรับการค้นหาส่วนที่ยังไม่ทดสอบของ codebase ความครอบคลุมในการทดสอบมีประโยชน์เพียงเล็กน้อยในการแสดงตัวเลขว่าการทดสอบของคุณดีเพียงใด
ดังนั้น เราขอแนะนำให้คุณติดตั้งและเรียกใช้ตัววิเคราะห์ความครอบคลุมหลังจากเขียนการทดสอบบางอย่าง รายงานที่มีบรรทัดโค้ดที่ไฮไลต์จะช่วยให้คุณเข้าใจเส้นทางการดำเนินการได้ดีขึ้นและค้นหาตำแหน่งที่ไม่เปิดเผยซึ่งควรครอบคลุม นอกจากนี้ เมื่อดูที่ผู้ตั้งรับ ผู้ตั้งค่า และส่วนหน้า คุณจะเห็นว่าเหตุใดการครอบคลุม 100% จึงไม่สนุก
6. เล่นเลโก้
บางครั้ง ฉันเห็นคำถามเช่น “ฉันจะทดสอบวิธีส่วนตัวได้อย่างไร” คุณทำไม่ได้ หากคุณถามคำถามนั้น แสดงว่ามีบางอย่างผิดพลาดไปแล้ว โดยปกติหมายความว่าคุณละเมิดหลักการความรับผิดชอบเดียว และโมดูลของคุณไม่ได้ทำอะไรอย่างถูกต้อง
ปรับโครงสร้างโมดูลนี้ใหม่และดึงตรรกะที่คุณคิดว่าสำคัญลงในโมดูลแยกต่างหาก ไม่มีปัญหาในการเพิ่มจำนวนไฟล์ ซึ่งจะทำให้โค้ดมีโครงสร้างเป็นตัวต่อเลโก้: อ่านง่ายมาก บำรุงรักษาได้ เปลี่ยนได้ และทดสอบได้
การจัดโครงสร้างโค้ดอย่างถูกต้องนั้นพูดง่ายกว่าทำ นี่คือคำแนะนำสองข้อ:
ฟังก์ชั่นการเขียนโปรแกรม
การเรียนรู้หลักการและแนวคิดของการเขียนโปรแกรมเชิงฟังก์ชันนั้นคุ้มค่า ภาษากระแสหลักส่วนใหญ่ เช่น C, C++, C#, Java, Assembly, JavaScript และ Python บังคับให้คุณเขียนโปรแกรมสำหรับเครื่อง การเขียนโปรแกรมเชิงหน้าที่เหมาะสมกับสมองของมนุษย์มากกว่า
สิ่งนี้อาจดูเหมือนขัดกับสัญชาตญาณในตอนแรก แต่ให้พิจารณาสิ่งนี้: คอมพิวเตอร์จะไม่เป็นไรถ้าคุณใส่รหัสทั้งหมดของคุณในวิธีเดียว ใช้กลุ่มหน่วยความจำที่ใช้ร่วมกันเพื่อเก็บค่าชั่วคราว และใช้คำสั่งข้ามในปริมาณที่พอเหมาะ นอกจากนี้ บางครั้งคอมไพเลอร์ในขั้นตอนการปรับให้เหมาะสมก็ทำเช่นนี้ อย่างไรก็ตาม สมองของมนุษย์ไม่สามารถจัดการกับวิธีนี้ได้ง่ายๆ
การเขียนโปรแกรมเชิงฟังก์ชันบังคับให้คุณเขียนฟังก์ชันล้วนๆ โดยไม่มีผลข้างเคียง ด้วยรูปแบบที่รุนแรง ในลักษณะที่แสดงออก ด้วยวิธีนี้ การให้เหตุผลเกี่ยวกับฟังก์ชันจะง่ายกว่ามาก เนื่องจากสิ่งเดียวที่สร้างคือค่าที่ส่งกลับ ตอน Podcast ของ Programming Throwdown การเขียนโปรแกรมเชิงฟังก์ชันกับ Adam Gordon Bell จะช่วยให้คุณได้รับความเข้าใจพื้นฐาน และคุณสามารถดำเนินการต่อกับตอน Corecursive ของ God's Programming Language กับ Philip Wadler และ Category Theory With Bartosz Milewski สองอันสุดท้ายเพิ่มพูนการรับรู้ของฉันเกี่ยวกับการเขียนโปรแกรมอย่างมาก
การพัฒนาที่ขับเคลื่อนด้วยการทดสอบ
ฉันแนะนำให้เชี่ยวชาญ TDD วิธีที่ดีที่สุดในการเรียนรู้คือการฝึกฝน String Calculator Kata เป็นวิธีที่ยอดเยี่ยมในการฝึกฝนโค้ด kata การเรียนรู้ kata ให้เชี่ยวชาญนั้นต้องใช้เวลา แต่ท้ายที่สุดแล้ว จะช่วยให้คุณซึมซับแนวคิดของ TDD ได้อย่างเต็มที่ ซึ่งจะช่วยให้คุณสร้างโค้ดที่มีโครงสร้างที่ดี ซึ่งน่ายินดีที่ได้ทำงานด้วยและสามารถทดสอบได้
ข้อควรระวังประการหนึ่ง: บางครั้งคุณจะเห็นผู้พิถีพิถัน TDD อ้างว่า TDD เป็นวิธีเดียวที่ถูกต้องในการเขียนโปรแกรม ในความคิดของฉัน มันเป็นเพียงเครื่องมือที่มีประโยชน์อีกอย่างในกล่องเครื่องมือของคุณ ไม่มีอะไรมากไปกว่านี้อีกแล้ว
บางครั้ง คุณจำเป็นต้องดูวิธีการปรับโมดูลและกระบวนการที่เกี่ยวข้องกัน และไม่รู้ว่าจะใช้ข้อมูลและลายเซ็นใด ในกรณีเช่นนี้ ให้เขียนโค้ดจนกว่าจะคอมไพล์ จากนั้นจึงเขียนการทดสอบเพื่อแก้ไขปัญหาและดีบักฟังก์ชันการทำงาน
ในกรณีอื่นๆ คุณทราบอินพุตและเอาต์พุตที่คุณต้องการ แต่ไม่รู้ว่าจะเขียนการใช้งานอย่างถูกต้องอย่างไรเนื่องจากตรรกะที่ซับซ้อน สำหรับกรณีเหล่านี้ ง่ายกว่าที่จะเริ่มต้นปฏิบัติตามขั้นตอน TDD และสร้างโค้ดของคุณทีละขั้นตอน แทนที่จะใช้เวลาคิดเกี่ยวกับการใช้งานที่สมบูรณ์แบบ
7. ให้การทดสอบง่ายและเน้น
ยินดีที่ได้ทำงานในสภาพแวดล้อมโค้ดที่เป็นระเบียบเรียบร้อยโดยไม่มีการรบกวนโดยไม่จำเป็น ด้วยเหตุนี้จึงเป็นสิ่งสำคัญที่จะนำหลักการ SOLID, KISS และ DRY ไปใช้กับการทดสอบ โดยใช้การปรับโครงสร้างใหม่เมื่อจำเป็น
บางครั้งฉันได้ยินความคิดเห็นเช่น "ฉันเกลียดการทำงานใน codebase ที่ได้รับการทดสอบอย่างหนัก เพราะทุกๆ การเปลี่ยนแปลงทำให้ฉันต้องแก้ไขการทดสอบหลายสิบครั้ง" นั่นเป็นปัญหาที่ต้องบำรุงรักษาสูงซึ่งเกิดจากการทดสอบที่ไม่ได้เน้นและพยายามทดสอบมากเกินไป หลักการ “ทำสิ่งหนึ่งให้ดี” ใช้กับการทดสอบด้วย: “ทดสอบสิ่งหนึ่งให้ดี”; การทดสอบแต่ละครั้งควรสั้นและทดสอบเพียงแนวคิดเดียว “ทดสอบสิ่งใดสิ่งหนึ่งให้ดี” ไม่ได้หมายความว่าคุณควรถูกจำกัดการยืนยันหนึ่งครั้งต่อการทดสอบ: คุณสามารถใช้หลายสิบข้อได้หากคุณกำลังทดสอบการแมปข้อมูลที่ไม่สำคัญและสำคัญ
โฟกัสนี้ไม่จำกัดเฉพาะการทดสอบหรือประเภทการทดสอบหนึ่งๆ ลองนึกภาพการจัดการกับตรรกะที่ซับซ้อนซึ่งคุณทดสอบโดยใช้การทดสอบหน่วย เช่น การแมปข้อมูลจากระบบ ERP กับโครงสร้างของคุณ และคุณมีการทดสอบการรวมที่เข้าถึง ERP API จำลองและส่งคืนผลลัพธ์ ในกรณีนั้น สิ่งสำคัญคือต้องจำไว้ว่าการทดสอบหน่วยของคุณครอบคลุมอะไรบ้าง เพื่อไม่ให้คุณทดสอบการแมปอีกครั้งในการทดสอบการรวม โดยปกติ ก็เพียงพอที่จะทำให้มั่นใจว่าผลลัพธ์มีฟิลด์การระบุที่ถูกต้อง
ด้วยโค้ดที่มีโครงสร้างเหมือนตัวต่อเลโก้และการทดสอบที่เน้น การเปลี่ยนแปลงตรรกะทางธุรกิจไม่ควรเจ็บปวด หากการเปลี่ยนแปลงรุนแรง คุณเพียงแค่วางไฟล์และการทดสอบที่เกี่ยวข้อง และทำการติดตั้งใช้งานใหม่ด้วยการทดสอบใหม่ ในกรณีที่มีการเปลี่ยนแปลงเล็กน้อย คุณมักจะเปลี่ยนการทดสอบหนึ่งถึงสามครั้งเพื่อให้เป็นไปตามข้อกำหนดใหม่และเปลี่ยนแปลงตรรกะ ไม่เป็นไรที่จะเปลี่ยนการทดสอบ คุณสามารถคิดเกี่ยวกับการปฏิบัตินี้เป็นการทำบัญชีแบบสองรายการ
วิธีอื่นๆ ในการบรรลุความเรียบง่าย ได้แก่:
- มากับข้อตกลงสำหรับโครงสร้างไฟล์ทดสอบ ทดสอบโครงสร้างเนื้อหา (โดยทั่วไปคือโครงสร้าง Arrange-Act-Assert) และการตั้งชื่อทดสอบ สิ่งสำคัญที่สุดคือการปฏิบัติตามกฎเหล่านี้อย่างสม่ำเสมอ
- แยกบล็อคโค้ดขนาดใหญ่ไปยังเมธอด เช่น "เตรียมคำขอ" และสร้างฟังก์ชันตัวช่วยสำหรับการดำเนินการซ้ำๆ
- การใช้รูปแบบตัวสร้างสำหรับการกำหนดค่าข้อมูลทดสอบ
- การใช้ (ในการทดสอบการรวม) คอนเทนเนอร์ DI เดียวกันกับที่คุณใช้ในแอปหลัก ดังนั้นทุกอินสแตนซ์จะไม่สำคัญเท่ากับ
TestServices.Get()โดยไม่ต้องสร้างการพึ่งพาด้วยตนเอง วิธีนี้จะง่ายต่อการอ่าน บำรุงรักษา และเขียนการทดสอบใหม่ เนื่องจากคุณมีตัวช่วยที่เป็นประโยชน์อยู่แล้ว
หากคุณรู้สึกว่าการทดสอบซับซ้อนเกินไป ให้หยุดและคิด โมดูลหรือการทดสอบของคุณต้องได้รับการจัดองค์ประกอบใหม่
8. ใช้เครื่องมือเพื่อทำให้ชีวิตของคุณง่ายขึ้น
คุณจะต้องเผชิญกับงานที่น่าเบื่อมากมายขณะทดสอบ ตัวอย่างเช่น การตั้งค่าสภาพแวดล้อมการทดสอบหรืออ็อบเจ็กต์ข้อมูล การกำหนดค่า stub และ mocks สำหรับการพึ่งพา และอื่นๆ โชคดีที่กองเทคโนโลยีสำหรับผู้ใหญ่ทุกกลุ่มมีเครื่องมือหลายอย่างที่จะทำให้งานเหล่านี้น่าเบื่อน้อยลง
ฉันแนะนำให้คุณเขียนการทดสอบร้อยรายการแรกหากยังไม่ได้ทำ จากนั้นใช้เวลาสักครู่เพื่อระบุงานที่ซ้ำซากและเรียนรู้เกี่ยวกับเครื่องมือที่เกี่ยวข้องกับการทดสอบสำหรับสแต็กเทคโนโลยีของคุณ
คุณสามารถใช้เครื่องมือต่อไปนี้เพื่อเป็นแรงบันดาลใจ:
- นักวิ่งทดสอบ. มองหาไวยากรณ์ที่กระชับและใช้งานง่าย จากประสบการณ์ของฉัน สำหรับ .NET ฉันขอแนะนำ xUnit (แม้ว่า NUnit ก็เป็นตัวเลือกที่ดีเช่นกัน) สำหรับ JavaScript หรือ TypeScript ฉันใช้ Jest พยายามหาคู่ที่ดีที่สุดสำหรับงานและความคิดของคุณเพราะเครื่องมือและความท้าทายมีวิวัฒนาการ
- ห้องสมุดล้อเลียน อาจมีการจำลองระดับต่ำสำหรับการพึ่งพาโค้ด เช่น อินเทอร์เฟซ แต่ยังมีการจำลองระดับสูงกว่าสำหรับเว็บ API หรือฐานข้อมูลด้วย สำหรับ JavaScript และ TypeScript การเยาะเย้ยระดับต่ำที่รวมอยู่ใน Jest นั้นใช้ได้ สำหรับ .NET ฉันใช้ Moq แม้ว่า NSubstitute ก็ยอดเยี่ยมเช่นกัน สำหรับ web API mocks ฉันสนุกกับการใช้ WireMock.NET สามารถใช้แทน API เพื่อแก้ไขปัญหาและดีบักการจัดการการตอบสนอง นอกจากนี้ยังเชื่อถือได้และรวดเร็วในการทดสอบอัตโนมัติ สามารถเยาะเย้ยฐานข้อมูลโดยใช้คู่หูในหน่วยความจำ EfCore ใน .NET มีตัวเลือกดังกล่าว
- ไลบรารีการสร้างข้อมูล โปรแกรมอรรถประโยชน์เหล่านี้เติมวัตถุข้อมูลของคุณด้วยข้อมูลสุ่ม มีประโยชน์ ตัวอย่างเช่น เมื่อคุณสนใจเพียงสองสามฟิลด์จากออบเจกต์การถ่ายโอนข้อมูลขนาดใหญ่ (หากเป็นเช่นนั้น คุณอาจต้องการทดสอบความถูกต้องของการแมปเท่านั้น) คุณสามารถใช้สำหรับการทดสอบและเป็นข้อมูลสุ่มเพื่อแสดงบนแบบฟอร์มหรือเพื่อกรอกฐานข้อมูลของคุณ เพื่อการทดสอบ ฉันใช้ AutoFixture ใน .NET
- ไลบรารีอัตโนมัติของ UI ผู้ใช้เหล่านี้เป็นผู้ใช้อัตโนมัติสำหรับการทดสอบอัตโนมัติ: พวกเขาสามารถเรียกใช้แอปของคุณ กรอกแบบฟอร์ม คลิกที่ปุ่ม อ่านป้ายกำกับ และอื่นๆ ในการนำทางองค์ประกอบทั้งหมดของแอป คุณไม่จำเป็นต้องจัดการกับการคลิกด้วยพิกัดหรือการจดจำภาพ แพลตฟอร์มหลักมีเครื่องมือในการค้นหาองค์ประกอบที่จำเป็นตามประเภท ตัวระบุ หรือข้อมูล ดังนั้นคุณไม่จำเป็นต้องเปลี่ยนการทดสอบด้วยการออกแบบใหม่ทุกครั้ง พวกมันแข็งแกร่ง ดังนั้นเมื่อคุณทำให้มันใช้งานได้สำหรับคุณและ CI (บางครั้ง คุณพบว่าสิ่งต่าง ๆ ใช้งานได้บน เครื่องของคุณ เท่านั้น ) พวกมันก็จะทำงานต่อไป ฉันสนุกกับการใช้ FlaUI สำหรับ .NET และ Cypress สำหรับ JavaScript และ TypeScript
- ไลบรารีการยืนยัน ตัวดำเนินการทดสอบส่วนใหญ่มีเครื่องมือยืนยัน แต่มีบางกรณีที่เครื่องมืออิสระสามารถช่วยคุณเขียนการยืนยันที่ซับซ้อนโดยใช้ไวยากรณ์ที่สะอาดกว่าและอ่านง่ายกว่า เช่น Fluent Assertions สำหรับ .NET ฉันชอบฟังก์ชันนี้เป็นพิเศษเพื่อยืนยันว่าคอลเล็กชันเท่ากันโดยไม่คำนึงถึงคำสั่งซื้อของไอเท็มหรือที่อยู่ในหน่วยความจำ
ขอให้กระแสน้ำจงสถิตอยู่กับท่าน
ความสุขเกิดขึ้นควบคู่ไปกับสิ่งที่เรียกว่าประสบการณ์ที่เรียกว่า "การไหล" ที่อธิบายไว้ในรายละเอียดในหนังสือ Flow: The Psychology of Optimal Experience เพื่อให้บรรลุประสบการณ์โฟลว์นั้น คุณต้องมีส่วนร่วมในกิจกรรมที่มีเป้าหมายที่ชัดเจนและสามารถเห็นความคืบหน้าของคุณได้ งานควรส่งผลให้เกิดการตอบกลับทันที ซึ่งการทดสอบอัตโนมัตินั้นเหมาะสมที่สุด คุณต้องสร้างสมดุลระหว่างความท้าทายและทักษะ ซึ่งขึ้นอยู่กับแต่ละคน การทดสอบ โดยเฉพาะอย่างยิ่งเมื่อเข้าใกล้กับ TDD สามารถช่วยแนะนำคุณและปลูกฝังความมั่นใจ สิ่งเหล่านี้ช่วยให้คุณกำหนดเป้าหมายที่เฉพาะเจาะจง โดยการทดสอบแต่ละครั้งที่ผ่านจะเป็นตัวบ่งชี้ความก้าวหน้าของคุณ
แนวทางการทดสอบที่ถูกต้องจะทำให้คุณมีความสุขและมีประสิทธิผลมากขึ้น และการทดสอบช่วยลดโอกาสที่อาการเหนื่อยหน่ายได้ กุญแจสำคัญคือการมองว่าการทดสอบเป็นเครื่องมือ (หรือชุดเครื่องมือ) ที่สามารถช่วยคุณในกิจวัตรประจำวันในการพัฒนา ไม่ใช่เป็นขั้นตอนที่เป็นภาระสำหรับการตรวจสอบโค้ดของคุณในอนาคต
การทดสอบเป็นส่วนที่จำเป็นของการเขียนโปรแกรมที่ช่วยให้วิศวกรซอฟต์แวร์สามารถปรับปรุงวิธีการทำงาน ให้ผลลัพธ์ที่ดีที่สุด และใช้เวลาอย่างเหมาะสมที่สุด ที่สำคัญกว่านั้น การทดสอบสามารถช่วยให้นักพัฒนาสนุกกับงานมากขึ้น ซึ่งจะเป็นการเพิ่มขวัญกำลังใจและแรงจูงใจของพวกเขา
