เครื่องมือประกอบธุรกิจ

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

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

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

เช่น

import { fibonacci, catalan } from '../src/math.js';
import { assert, test, suite } from 'a-made-up-testing-library';

suite('math tests', () => {
  test('fibonacci function', () => {
    // check expected fibonacci numbers against our known actual values
    // with an explanation if the values don't match
    assert.equal(fibonacci(0), 0, 'Invalid 0th fibonacci result');
    assert.equal(fibonacci(13), 233, 'Invalid 13th fibonacci result');
  });
  test('relationship between sequences', () => {
    // catalan numbers are greater than fibonacci numbers (but not equal)
    assert.isAbove(catalan(4), fibonacci(4));
  });
  test('bugfix: check bug #4141', () => {
    assert.isFinite(fibonacci(0)); // fibonacci(0) was returning NaN
  })
});

ตัวอย่างนี้สร้างกลุ่มการทดสอบ (บางครั้งเรียกว่าชุด) ที่เรียกว่า "การทดสอบคณิตศาสตร์" และกำหนดกรอบการทดสอบอิสระ 3 กรณีซึ่งแต่ละรายการเรียกใช้การยืนยัน กรอบการทดสอบเหล่านี้มักจะสามารถจัดการหรือเรียกใช้แต่ละรายการได้ เช่น ด้วยการใช้แฟล็กตัวกรองในตัวดำเนินการทดสอบ

ผู้ช่วย Assertion แบบพื้นฐาน

เฟรมเวิร์กการทดสอบส่วนใหญ่ รวมถึง Vitest จะมีคอลเล็กชันตัวช่วยการยืนยันในออบเจ็กต์ assert ซึ่งจะช่วยให้คุณตรวจสอบค่าของผลลัพธ์หรือสถานะอื่นๆ กับexpectationบางอย่างได้อย่างรวดเร็ว ความคาดหวังนี้มักเป็นค่า ที่ "เป็นที่ทราบกันดี" ในตัวอย่างก่อนหน้านี้ เราทราบว่าหมายเลข Fibonacci ที่ 13 ควรเป็น 233 เราจึงยืนยันได้ว่าการใช้ assert.equal โดยตรง

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

  • การตรวจสอบ "truthy" หรือที่มักเรียกกันว่าเป็นการตรวจสอบ "ตกลง" จะตรวจสอบว่าเงื่อนไขหนึ่งๆ เป็นจริงหรือไม่ โดยตรงกับวิธีเขียน if ที่ใช้ตรวจสอบดูว่าบางสิ่งสำเร็จหรือถูกต้องหรือไม่ ซึ่งมักจะระบุเป็น assert(...) หรือ assert.ok(...) และจะใช้ค่าเดียวบวกกับความคิดเห็นที่ไม่บังคับ

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

    • JavaScript จะแยกความเท่าเทียม (==) กับความเท่ากันแบบเข้มงวด (===) ไลบรารีการทดสอบส่วนใหญ่จะมีเมธอด assert.equal และ assert.strictEqual ตามลำดับ
  • การตรวจสอบคุณภาพระดับลึก ซึ่งขยายขอบเขตการตรวจสอบความเท่าเทียมกันไปยังการตรวจสอบเนื้อหาของออบเจ็กต์ อาร์เรย์ และข้อมูลประเภทอื่นๆ ที่ซับซ้อนกว่า รวมถึงตรรกะภายในสำหรับข้ามผ่านออบเจ็กต์เพื่อเปรียบเทียบ สิ่งเหล่านี้สำคัญเพราะ JavaScript ไม่มีวิธีในตัวสำหรับเปรียบเทียบเนื้อหาของออบเจ็กต์หรืออาร์เรย์ 2 รายการ ตัวอย่างเช่น [1,2,3] == [1,2,3] จะเป็นเท็จเสมอ เฟรมเวิร์กการทดสอบมักจะประกอบด้วยตัวช่วย deepEqual หรือ deepStrictEqual

เครื่องมือช่วยยืนยันที่เปรียบเทียบค่า 2 ค่า (ไม่ใช่เครื่องหมายถูก "ที่เชื่อถือได้" เท่านั้น) โดยทั่วไปจะใช้อาร์กิวเมนต์ 2 หรือ 3 ตัว

  • ค่าจริงที่สร้างจากโค้ดที่อยู่ระหว่างการทดสอบหรืออธิบายสถานะที่จะตรวจสอบ
  • ค่าที่คาดไว้ ซึ่งโดยปกติแล้วจะเป็นฮาร์ดโค้ด (เช่น ตัวเลขหรือสตริงตามตัวอักษร)
  • ความคิดเห็น (ไม่บังคับ) ซึ่งอธิบายสิ่งที่คาดหวังหรือสิ่งที่อาจล้มเหลว ซึ่งจะรวมไว้ด้วยหากบรรทัดนี้ล้มเหลว

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

  test('JWT parse', () => {
    const json = decodeJwt('eyJieSI6InNhbXRob3Ii…');

    assert.ok(json.payload.admin, 'user should be admin');
    assert.deepEqual(json.payload.groups, ['role:Admin', 'role:Submitter']);
    assert.equal(json.header.alg, 'RS265')
    assert.isAbove(json.payload.exp, +new Date(), 'expiry must be in future')
  });

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

การยืนยันความคล่องแคล่วและ BDD

นักพัฒนาซอฟต์แวร์บางรายชอบรูปแบบการยืนยันที่อาจเรียกว่าการพัฒนาตามพฤติกรรม (BDD) หรือการยืนยันแบบ Fluent ตัวช่วยเหล่านี้เรียกอีกอย่างหนึ่งว่าตัวช่วย "ความคาดหวัง" เนื่องจากจุดแรกเข้าของการตรวจสอบความคาดหวังเป็นวิธีการที่ชื่อ expect()

คาดว่าผู้ช่วยจะทำงานในลักษณะเดียวกับการยืนยันที่เขียนขึ้นโดยใช้เมธอดที่เข้าใจง่าย เช่น assert.ok หรือ assert.strictDeepEquals แต่นักพัฒนาซอฟต์แวร์บางรายพบว่าการนำไปใช้อ่านง่ายขึ้น การยืนยัน BDD อาจมีลักษณะดังนี้

// A failure here would generate "Expect result to be an array that does include 42"
const result = await possibleMeaningsOfLife();
expect(result).to.be.an('array').that.does.include(42);

// or a simpler form
expect(result).toBe('array').toContainEqual(42);

// the same in assert might be
assert.typeOf(result, 'array', 'Expected the result to be an array');
assert.include(result, 42, 'Expected the result to include 42');

รูปแบบการยืนยันเหล่านี้ใช้งานได้เพราะเทคนิคที่เรียกว่า Method Chaining ซึ่งจะเชื่อมโยงออบเจ็กต์ที่ expect แสดงผลอย่างต่อเนื่องพร้อมกับการเรียกใช้เมธอดเพิ่มเติม บางส่วนของการโทร เช่น to.be และ that.does ในตัวอย่างก่อนหน้านี้ไม่มีฟังก์ชันและมีเพียงการทำให้การโทรอ่านง่ายขึ้น และอาจสร้างความคิดเห็นอัตโนมัติได้หากการทดสอบล้มเหลว (โดยปกติแล้ว expect จะไม่รองรับความคิดเห็นที่ไม่บังคับ เนื่องจากการใส่ห่วงโซ่ควรอธิบายความล้มเหลวอย่างชัดเจน)

กรอบการทดสอบจำนวนมากรองรับทั้ง Fluent/BDD และการยืนยันปกติ Vitest เช่น ส่งออกแนวทางของทั้ง 2 แนวทางของจีนและมีแนวทางที่กระชับยิ่งขึ้นของตนเองสำหรับ BDD เล็กน้อย ในทางกลับกัน เจสต์จะรวมเฉพาะเมธอดความคาดหวังโดยค่าเริ่มต้น

จัดกลุ่มการทดสอบในไฟล์ต่างๆ

ในการเขียนการทดสอบ เรามักจะจัดกลุ่มแบบโดยนัยอยู่แล้ว แทนที่จะต้องเขียนการทดสอบทั้งหมดในไฟล์เดียว โดยทั่วไปแล้วมักจะเขียนการทดสอบในไฟล์หลายไฟล์ อันที่จริง นักดำเนินการทดสอบมักจะทราบแค่ว่าไฟล์มีไว้สำหรับการทดสอบเพราะตัวกรองหรือนิพจน์ทั่วไปที่กำหนดไว้ล่วงหน้า ตัวอย่างเช่น Vitest มีไฟล์ทั้งหมดในโปรเจ็กต์ที่ลงท้ายด้วยส่วนขยายอย่าง ".test.jsx" หรือ ".spec.ts" (".test" และ ".spec" บวกด้วยนามสกุลที่ถูกต้องอีกจำนวนหนึ่ง)

การทดสอบคอมโพเนนต์มักจะอยู่ในไฟล์เพียร์ไปยังคอมโพเนนต์ที่อยู่ระหว่างการทดสอบ ดังเช่นในโครงสร้างไดเรกทอรีต่อไปนี้

รายการไฟล์ในไดเรกทอรี รวมถึง UserList.tsx และ UserList.test.tsx
ไฟล์คอมโพเนนต์และไฟล์ทดสอบที่เกี่ยวข้อง

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

จัดกลุ่มการทดสอบภายในไฟล์

ตามที่ใช้ในตัวอย่างก่อนหน้านี้ เป็นเรื่องปกติที่จะทดสอบภายในการเรียกไปยัง suite() ซึ่งจัดกลุ่มการทดสอบที่คุณตั้งค่าด้วย test() ตามปกติแล้ว ห้องสวีทจะไม่ใช่การทดสอบด้วยตนเอง แต่ช่วยให้มีโครงสร้างโดยการจัดกลุ่มการทดสอบหรือเป้าหมายที่เกี่ยวข้องโดยการเรียกใช้เมธอด "ผ่าน" สำหรับ test() วิธีการผ่านจะอธิบายการดำเนินการของการทดสอบเอง

การทดสอบแบบ Fluent/BDD มีความเท่าเทียมกันตามมาตรฐานเช่นเดียวกับการยืนยัน ตัวอย่างทั่วไปบางส่วนเปรียบเทียบในโค้ดต่อไปนี้

// traditional/TDD
suite('math tests', () => {
  test('handle zero values', () => {
    assert.equal(fibonacci(0), 0);
  });
});

// Fluent/BDD
describe('math tests', () => {
  it('should handle zero values', () => {
    expect(fibonacci(0)).toBe(0);
  });
})

ในเฟรมเวิร์กส่วนใหญ่ suite และ describe จะทำงานคล้ายๆ กับ test และ it ซึ่งต่างจากการใช้ expect และ assert ในการเขียนการยืนยันมากกว่า

เครื่องมืออื่นๆ มีวิธีการจัดการชุดโปรแกรมและการทดสอบแตกต่างกันเล็กน้อย เช่น ตัวดำเนินการทดสอบในตัวของ Node.js รองรับการเรียกที่ซ้อนกันไปยัง test() เพื่อสร้างลำดับชั้นการทดสอบโดยนัย อย่างไรก็ตาม Vitest อนุญาตให้มีการซ้อนประเภทนี้โดยใช้ suite() เท่านั้น และจะไม่เรียกใช้ test() ที่กําหนดไว้ใน test() อื่น

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

วิธีการสำหรับวงจร

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

สำหรับทุกๆ "test()" หรือ "it()" ครั้งเดียวสำหรับห้องสวีท
ก่อนทำการทดสอบ "beforeindividual()" "beforeAll()"
หลังการทดสอบ "Afterindividual()" "AfterAll()"

ตัวอย่างเช่น คุณอาจต้องการเติมฐานข้อมูลผู้ใช้เสมือนล่วงหน้าก่อนการทดสอบแต่ละครั้ง และล้างข้อมูลนั้นในภายหลัง

suite('user test', () => {
  beforeEach(() => {
    insertFakeUser('bob@example.com', 'hunter2');
  });
  afterEach(() => {
    clearAllUsers();
  });

  test('bob can login', async () => { … });
  test('alice can message bob', async () => { … });
});

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

คำแนะนำทั่วไป

ต่อไปนี้คือเคล็ดลับบางประการที่ควรจดจำเมื่อนึกถึงพื้นฐานเหล่านี้

แบบพื้นฐานเป็นแนวทาง

อย่าลืมว่าเครื่องมือและค่ากำหนดพื้นฐานในส่วนนี้และในอีก 2-3 หน้าจะไม่ตรงกับ Vitest, Jest, Mocha หรือ Web Test Runner หรือเฟรมเวิร์กเฉพาะอื่นๆ แม้ว่าเราจะใช้ Vitest เป็นแนวทางทั่วไป แต่อย่าลืมแมปข้อมูลดังกล่าวกับเฟรมเวิร์กที่คุณเลือก

ผสมผสานการยืนยันได้ตามต้องการ

การทดสอบคือโค้ดพื้นฐานที่อาจทำให้เกิดข้อผิดพลาด นักวิ่งทุกคนจะระบุเกณฑ์พื้นฐาน test() ซึ่งช่วยอธิบายกรอบการทดสอบที่แตกต่างกัน

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

การตั้งค่า IDE อาจช่วยชีวิตได้

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

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

// some.test.js
test('using test as a global', () => { … });

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

// some.test.js
import { test } from 'vitest';
test('using test as an import', () => { … });