การทดสอบคืออะไร

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

การทดสอบที่ประสบความสำเร็จทำให้คุณมั่นใจได้ว่าเมื่อคุณเพิ่มโค้ด ฟีเจอร์ใหม่ๆ หรือแม้กระทั่งอัปเกรดทรัพยากร 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 นโยบายนี้อาจจับคู่กับ "คอมโพเนนต์รีแอ็กชัน" อย่างแท้จริง แต่อาจมีความหมายต่างกันสำหรับนักพัฒนาแอปในบริบทอื่นๆ

ขนาดของการทดสอบแต่ละครั้งอาจแสดงถึงการทดสอบนั้นๆ ในแนวคิดที่มักเรียกว่า "พีระมิดทดสอบ" ซึ่งเป็นกฎทั่วไปสำหรับสิ่งที่ระบบตรวจสอบและวิธีการตรวจสอบ

พีระมิดทดสอบซึ่งมีการทดสอบจากต้นทางถึงปลายทาง (E2E) อยู่ด้านบน การทดสอบการผสานรวมอยู่ที่ตรงกลาง และการทดสอบ 1 หน่วยที่ด้านล่าง
พีระมิดทดสอบ

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

เราจะขยายการให้บริการประเภทเหล่านี้ไปยังประเภทการทดสอบอัตโนมัติ

ทดสอบความเข้าใจ

ไลบรารีและเฟรมเวิร์กการทดสอบส่วนใหญ่มีพื้นฐานอะไร

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