ขณะเขียนซอฟต์แวร์ คุณสามารถยืนยันได้ว่าซอฟต์แวร์ทำงานได้อย่างถูกต้องผ่านทางการทดสอบ การทดสอบสามารถนิยามได้กว้างๆ ว่าเป็นกระบวนการเรียกใช้ซอฟต์แวร์ในบางลักษณะเพื่อให้แน่ใจว่าซอฟต์แวร์จะทำงานได้ตามที่ต้องการ
การทดสอบที่ประสบความสำเร็จทำให้คุณมั่นใจได้ว่าเมื่อคุณเพิ่มโค้ด ฟีเจอร์ใหม่ๆ หรือแม้กระทั่งอัปเกรดทรัพยากร Dependency แล้ว ซอฟต์แวร์ที่คุณเขียนไว้แล้วจะยังทำงานต่อไปในแบบที่คุณคาดหวัง การทดสอบยังช่วยป้องกันซอฟต์แวร์ จากสถานการณ์ที่ไม่น่าเป็นไปได้หรืออินพุตที่ไม่คาดคิดอีกด้วย
ตัวอย่างลักษณะการทำงานบนเว็บที่คุณอาจต้องการทดสอบมีดังนี้
- ตรวจสอบว่าฟีเจอร์ของเว็บไซต์ทำงานอย่างถูกต้องเมื่อมีการคลิกปุ่ม
- การยืนยันว่าฟังก์ชันที่ซับซ้อนจะให้ผลลัพธ์ที่ถูกต้อง
- การทำงานที่ผู้ใช้ต้องเข้าสู่ระบบ
- ตรวจสอบว่าแบบฟอร์มรายงานข้อผิดพลาดอย่างถูกต้องเมื่อป้อนข้อมูลที่มีรูปแบบไม่ถูกต้อง
- การตรวจสอบว่าเว็บแอปที่ซับซ้อนยังคงทำงานได้เมื่อผู้ใช้มีแบนด์วิดท์ต่ำมากหรือออฟไลน์
การทดสอบอัตโนมัติกับการทดสอบด้วยตนเอง
คุณจะทดสอบซอฟต์แวร์ได้ 2 วิธี ได้แก่ การทดสอบอัตโนมัติและการทดสอบด้วยตนเอง
การทดสอบด้วยตนเองเกี่ยวข้องกับการที่มนุษย์ใช้ซอฟต์แวร์โดยตรง เช่น การโหลดเว็บไซต์ในเบราว์เซอร์และการยืนยันว่าซอฟต์แวร์ทำงานได้ตามที่คาดไว้ การทดสอบด้วยตนเองนั้นสร้างหรือหาคำจำกัดความได้ง่ายๆ เช่น เว็บไซต์โหลดได้ไหม คุณดำเนินการเหล่านี้ได้ไหม แต่การทำงานแต่ละครั้งมีค่าใช้จ่ายมหาศาลที่ต้องใช้เวลาอย่างมาก แม้ว่ามนุษย์จะมีความคิดสร้างสรรค์อย่างมากซึ่งช่วยให้ทดสอบประเภทที่เรียกว่าการทดสอบแบบสำรวจได้ แต่เราก็ยังเห็นความล้มเหลวหรือความไม่สอดคล้องกันอยู่ดี โดยเฉพาะอย่างยิ่งเมื่อทำงานเดิมๆ หลายครั้ง
การทดสอบอัตโนมัติคือกระบวนการที่ช่วยให้คอมพิวเตอร์แปลงรหัสและทำการทดสอบซ้ำได้เพื่อยืนยันลักษณะการทำงานของซอฟต์แวร์โดยไม่ให้มนุษย์ทำขั้นตอนซ้ำๆ เช่น การตั้งค่าหรือการตรวจสอบผลลัพธ์ ที่สำคัญคือเมื่อกำหนดค่าการทดสอบอัตโนมัติแล้ว การทดสอบจะทำงานได้บ่อยครั้ง นี่ยังเป็นคำจำกัดความที่กว้างมาก และควรสังเกตว่าการทดสอบอัตโนมัตินั้นใช้หลายรูปแบบและหลายรูปแบบ ส่วนใหญ่แล้วหลักสูตรนี้จะเกี่ยวข้องกับ การทดสอบอัตโนมัติโดยถือเป็นการฝึกฝน
การทดสอบด้วยตนเองมีมากกว่า 1 ตำแหน่ง ซึ่งมักจะเป็นข้อมูลเบื้องต้นในการเขียนการทดสอบอัตโนมัติ แต่ก็ยังเมื่อการทดสอบอัตโนมัติไม่น่าเชื่อถือ กำหนดขอบเขตกว้างๆ หรือเขียนได้ยากเกินไป
อธิบายหลักการพื้นฐานผ่านตัวอย่าง
สำหรับเรา ในฐานะนักพัฒนาเว็บที่เขียน JavaScript หรือภาษาที่เกี่ยวข้อง การทดสอบอัตโนมัติที่สั้นกระชับอาจเป็นสคริปต์ที่เรียกใช้ทุกวัน เช่น เรียกใช้ผ่านโหนด หรือโหลดในเบราว์เซอร์
import { fibonacci } from "../src/math.js";
if (fibonacci(0) !== 0) {
throw new Error("Invalid 0th fibonacci result");
}
const fib13 = fibonacci(13);
if (fib13 !== 233) {
throw new Error("Invalid 13th fibonacci result, was=${fib13} wanted=233");
}
นี่คือตัวอย่างง่ายๆ ที่จะให้ข้อมูลเชิงลึกต่อไปนี้
การดำเนินการนี้เป็นการทดสอบเนื่องจากเรียกใช้ซอฟต์แวร์บางอย่าง (ฟังก์ชัน Fibonacci) และดูแลให้ลักษณะการทำงานทำงานได้อย่างที่ควรจะเป็นโดยการตรวจสอบผลลัพธ์เทียบกับค่าที่คาดไว้ หากการทำงานไม่ถูกต้อง จะทำให้เกิดข้อผิดพลาด ซึ่ง JavaScript จะแสดงด้วยการใส่
Error
แม้ว่าคุณจะเรียกใช้สคริปต์นี้ด้วยตนเองในเทอร์มินัลหรือเบราว์เซอร์ แต่การทดสอบนี้ยังคงเป็นการทดสอบอัตโนมัติ เนื่องจากเรียกใช้ซ้ำๆ ได้โดยไม่ต้องทำตามขั้นตอนใดขั้นตอนหนึ่ง หน้าถัดไปจะอธิบายรายละเอียดเพิ่มเติม ในส่วนที่ทำการทดสอบ
แม้ว่าการทดสอบนี้ไม่ได้ใช้ไลบรารีใดๆ แต่เป็น JavaScript ที่ทำงานได้ทุกที่ แต่ก็ยังเป็นการทดสอบอยู่ มีเครื่องมือมากมายที่ช่วยคุณเขียนการทดสอบได้ รวมถึงเครื่องมือที่จะกล่าวถึงภายหลังในหลักสูตรนี้ แต่เครื่องมือทั้งหมดยังคงทำงานบนหลักการพื้นฐานในการก่อให้เกิดข้อผิดพลาดหากมีบางอย่างผิดพลาด
การทดสอบไลบรารีในทางปฏิบัติ
ไลบรารีหรือเฟรมเวิร์กการทดสอบในตัวส่วนใหญ่มีพื้นฐานหลัก 2 แบบที่ทำให้การทดสอบเขียนได้ง่ายขึ้น ได้แก่ การยืนยัน และวิธีกำหนดการทดสอบอิสระ เราจะอธิบายเรื่องนี้อย่างละเอียดในส่วนถัดไป ซึ่งก็คือการยืนยันและพื้นฐานอื่นๆ อย่างไรก็ตาม ในระดับสูง คุณต้องจำไว้ว่าการทดสอบเกือบทั้งหมดที่คุณเห็นหรือเขียนท้ายที่สุดก็มีการใช้แบบพื้นฐานเหล่านี้
การยืนยันคือวิธีรวมการตรวจสอบผลลัพธ์และก่อให้เกิดข้อผิดพลาดหากมีบางอย่างผิดปกติ ตัวอย่างเช่น คุณสามารถทำการทดสอบก่อนหน้านี้ให้กระชับยิ่งขึ้นได้โดยแนะนำ assert
ดังนี้
import { fibonacci } from "../src/math.js";
import { assert } from "a-made-up-testing-library";
assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
คุณปรับปรุงการทดสอบนี้เพิ่มเติมได้โดยกำหนดการทดสอบอิสระ หรือจะจัดกลุ่มเป็นชุดก็ได้ (ไม่บังคับ) ชุดโปรแกรมต่อไปนี้จะทดสอบฟังก์ชัน Fibonacci และฟังก์ชันคาตาลันอย่างอิสระ
import { fibonacci, catalan } from "../src/math.js";
import { assert, test, suite } from "a-made-up-testing-library";
suite("math tests", () => {
test("fibonacci function", () => {
assert.equal(fibonacci(0), 0, "Invalid 0th fibonacci result");
assert.equal(fibonacci(13), 233, "Invalid 13th fibonacci result");
});
test("relationship between sequences", () => {
const numberToCheck = 4;
const fib = fibonacci(numberToCheck);
const cat = catalan(numberToCheck);
assert.isAbove(fib, cat);
});
});
ในบริบทของการทดสอบซอฟต์แวร์นี้ คำว่า test เป็นคำนามหมายถึง test Case ซึ่งเป็นสถานการณ์เดี่ยวที่ระบุได้อย่างเป็นอิสระและระบุที่อยู่ได้ เช่น กรณีทดสอบ "relationship between sequences" ในตัวอย่างก่อนหน้านี้
การทดสอบที่มีชื่อแยกกันจะมีประโยชน์สำหรับงานต่อไปนี้ รวมถึงงานอื่นๆ ด้วย
- การกำหนดวิธีการทดสอบที่สำเร็จหรือล้มเหลวเมื่อเวลาผ่านไป
- การไฮไลต์ข้อบกพร่องหรือสถานการณ์ตามชื่อเพื่อให้คุณทดสอบว่าสถานการณ์ได้รับการแก้ไขได้ง่ายขึ้นหรือไม่
- ทำการทดสอบบางรายการแยกจากการทดสอบอื่น เช่น ทดสอบผ่านตัวกรอง glob
วิธีหนึ่งในการพิจารณาตัวอย่างกรอบการทดสอบคือการใช้ "3 A" ของการทดสอบ 1 หน่วย นั่นคือ จัดเรียง ดำเนินการ และกล่าวอ้าง โดยหลักแล้ว กรอบการทดสอบแต่ละรายการจะมีลักษณะดังต่อไปนี้
- จัดเรียงค่าหรือสถานะบางอย่าง (ซึ่งอาจเป็นแค่ข้อมูลอินพุตฮาร์ดโค้ด)
- ดำเนินการบางอย่าง เช่น เรียกใช้เมธอด
- ยืนยันค่าเอาต์พุตหรือสถานะที่อัปเดต (โดยใช้
assert
)
ระดับการทดสอบ
ตัวอย่างโค้ดในส่วนก่อนหน้านี้อธิบายการทดสอบหน่วย เนื่องจากตัวอย่างดังกล่าวจะทดสอบส่วนเล็กๆ ของซอฟต์แวร์ โดยมักจะโฟกัสที่ไฟล์เดียว และในกรณีนี้จะเป็นเพียงเอาต์พุตจากฟังก์ชันเดียว ความซับซ้อนของการทดสอบเพิ่มมากขึ้นเมื่อคุณพิจารณาโค้ดจากไฟล์ คอมโพเนนต์ หรือแม้แต่ระบบที่เชื่อมต่อกันหลายๆ ระบบ (บางครั้งก็อยู่นอกเหนือการควบคุมของคุณ เช่น บริการเครือข่ายหรือลักษณะการทำงานของทรัพยากร Dependency ภายนอก) ด้วยเหตุนี้ ระบบจึงมักตั้งชื่อประเภทการทดสอบตามขอบเขตหรือสเกล
นอกจากการทดสอบหน่วยแล้ว ตัวอย่างบางส่วนของการทดสอบประเภทอื่นๆ ได้แก่ การทดสอบคอมโพเนนต์ การทดสอบภาพ และการทดสอบการผสานรวม ชื่อเหล่านี้ไม่มีคำจำกัดความที่ชัดเจนและอาจมีความหมายแตกต่างกันไปตามฐานของโค้ด ดังนั้น อย่าลืมใช้ชื่อเหล่านั้นเป็นแนวทางและคิดคำจำกัดความที่เหมาะกับคุณ ตัวอย่างเช่น คอมโพเนนต์ที่อยู่ภายใต้การทดสอบในระบบของคุณคืออะไร สำหรับนักพัฒนาซอฟต์แวร์ React นโยบายนี้อาจจับคู่กับ "คอมโพเนนต์รีแอ็กชัน" อย่างแท้จริง แต่อาจมีความหมายต่างกันสำหรับนักพัฒนาแอปในบริบทอื่นๆ
ขนาดของการทดสอบแต่ละครั้งอาจแสดงถึงการทดสอบนั้นๆ ในแนวคิดที่มักเรียกว่า "พีระมิดทดสอบ" ซึ่งเป็นกฎทั่วไปสำหรับสิ่งที่ระบบตรวจสอบและวิธีการตรวจสอบ
ไอเดียนี้เกิดขึ้นซ้ำๆ และตอนนี้ก็มีรูปทรงอื่นๆ ที่ได้รับความนิยมแล้ว เช่น ข้าวหลามตัดแบบทดสอบ หรือไอศกรีมโคนทดสอบ ลำดับความสำคัญในการเขียนทดสอบของคุณจะไม่ซ้ำกันสำหรับฐานของโค้ด อย่างไรก็ตาม ฟีเจอร์ที่พบบ่อยคือการทดสอบที่ง่ายกว่า เช่น การทดสอบ 1 หน่วย มีแนวโน้มที่จะทำงานได้เร็วกว่า เขียนง่ายกว่า (คุณจึงมีมากกว่า) และทดสอบขอบเขตที่จำกัด ในขณะที่การทดสอบที่ซับซ้อนอย่างการทดสอบตั้งแต่ต้นจนจบจะเขียนได้ยาก แต่สามารถทดสอบขอบเขตที่กว้างขึ้นได้ อันที่จริง ชั้นบนสุดของ "รูปร่าง" การทดสอบหลายๆ รูปแบบมักจะเป็นการทดสอบด้วยตนเอง เนื่องจากการโต้ตอบของผู้ใช้บางอย่างอาจซับซ้อนเกินไปที่จะนำไปจัดเป็นการทดสอบอัตโนมัติ
เราจะขยายการให้บริการประเภทเหล่านี้ไปยังประเภทการทดสอบอัตโนมัติ
ทดสอบความเข้าใจ
ไลบรารีและเฟรมเวิร์กการทดสอบส่วนใหญ่มีพื้นฐานอะไร
assert()
และรูปแบบของโฆษณามักรวมอยู่ด้วย เนื่องจากจะทำให้การตรวจสอบเขียนได้ง่ายขึ้นtest()
จะรวมอยู่ในตัวดำเนินการทดสอบเกือบทั้งหมด และโค้ดทดสอบมีความสำคัญเนื่องจากโค้ดทดสอบไม่ได้ทำงานที่ระดับบนสุดของไฟล์ ซึ่งทำให้ตัวดำเนินการทดสอบจัดการกรณีทดสอบแต่ละกรณีเป็นหน่วยอิสระได้