Công cụ thương mại

Kiểm thử tự động về cơ bản chỉ là mã sẽ gửi hoặc gây ra lỗi nếu đã xảy ra sự cố. Hầu hết thư viện hoặc khung kiểm thử đều cung cấp nhiều các dữ liệu nguyên thuỷ giúp viết mã kiểm thử dễ dàng hơn.

Như đã đề cập trong phần trước, các dữ liệu gốc này hầu như luôn bao gồm xác định các bài kiểm thử độc lập (được gọi là trường hợp kiểm thử) và cung cấp xác nhận. Xác nhận là một cách kết hợp việc kiểm tra kết quả và gửi nếu có gì đó không đúng và có thể được coi là dữ liệu gốc cơ bản của tất cả dữ liệu gốc kiểm thử.

Trang này trình bày cách tiếp cận chung đối với các dữ liệu gốc này. Khung mà bạn đã chọn có thể có thông tin như thế này nhưng đây không phải là thông tin tham khảo chính xác.

Ví dụ:

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
  })
});

Ví dụ này tạo ra một nhóm các bài kiểm thử (đôi khi còn gọi là bộ kiểm thử) được gọi là "math" kiểm thử" và xác định 3 trường hợp kiểm thử độc lập, trong đó mỗi trường hợp sẽ chạy một số câu nhận định. Thường thì những trường hợp kiểm thử này có thể được xử lý hoặc chạy riêng lẻ, chẳng hạn như bằng một cờ bộ lọc trong trình chạy kiểm thử của bạn.

Trình trợ giúp xác nhận dưới dạng dữ liệu gốc

Hầu hết các khung kiểm thử, bao gồm cả Vitest, đều có một tập hợp các câu nhận định trình trợ giúp trên đối tượng assert cho phép bạn kiểm tra nhanh các giá trị trả về hoặc khác với một số kỳ vọng. Kỳ vọng đó thường là "được biết đến" giá trị. Trong ví dụ trước, chúng ta biết số Fibonacci thứ 13 phải là 233, vì vậy chúng ta có thể xác nhận điều đó trực tiếp bằng cách sử dụng assert.equal.

Có thể bạn cũng có kỳ vọng rằng một giá trị có một dạng nhất định hoặc lớn hơn giá trị khác hoặc có một thuộc tính khác. Khoá học này bao gồm đầy đủ các trình trợ giúp xác nhận, nhưng các khung kiểm thử luôn cung cấp ít nhất các bước kiểm tra cơ bản sau đây:

  • Một "sự thật" kiểm tra, thường được mô tả là 'ok' kiểm tra, kiểm tra để đảm bảo rằng một điều kiện là đúng, phù hợp với cách bạn có thể viết if để kiểm tra xem một nội dung nào đó có thành công hay không chính xác. Thông tin này thường được cung cấp dưới dạng assert(...) hoặc assert.ok(...), và nhận một giá trị duy nhất cùng với một chú thích tuỳ chọn.

  • Một kiểm tra đẳng thức, chẳng hạn như trong ví dụ kiểm tra toán học, trong đó bạn dự kiến giá trị trả về hoặc trạng thái của một đối tượng bằng một giá trị tốt đã biết. Những quảng cáo này dành cho đẳng thức gốc (chẳng hạn như cho số và chuỗi) hoặc đẳng thức tham chiếu (có phải là cùng một đối tượng). Về cơ bản, đây chỉ là "sự thật" dấu kiểm khi so sánh với == hoặc ===.

    • JavaScript phân biệt giữa đẳng thức rời (==) và đẳng thức nghiêm ngặt (===). Hầu hết các thư viện kiểm thử đều cung cấp cho bạn các phương thức assert.equalassert.strictEqual.
  • Kiểm tra bình đẳng sâu, mở rộng kiểm tra đẳng thức để bao gồm cả việc kiểm tra nội dung của đối tượng, mảng và các kiểu dữ liệu phức tạp khác cũng như logic nội bộ để truyền tải các đối tượng nhằm so sánh chúng. Đây là những thông tin quan trọng bởi vì JavaScript không có cách thức tích hợp để so sánh nội dung của hai đối tượng hoặc mảng. Ví dụ: [1,2,3] == [1,2,3] luôn là false. Thử nghiệm các khung thường bao gồm trình trợ giúp deepEqual hoặc deepStrictEqual.

Trình trợ giúp xác nhận so sánh hai giá trị (thay vì chỉ kiểm tra "sự thật") thường có 2 hoặc 3 đối số:

  • Giá trị thực tế, như được tạo từ mã đang được kiểm thử hoặc mô tả trạng thái để xác thực.
  • Giá trị dự kiến, thường được cố định giá trị trong mã (ví dụ: giá trị cố định hoặc chuỗi).
  • Nhận xét tùy chọn mô tả những gì được mong đợi hoặc những gì có thể đã không thành công, giá trị này sẽ được thêm vào nếu đường này không thành công.

Đây cũng là một phương pháp khá phổ biến để kết hợp các câu nhận định để tạo nhiều kiểm tra, vì hiếm khi ai có thể xác nhận chính xác trạng thái hệ thống. Ví dụ:

  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 sử dụng thư viện câu nhận định Chai để cung cấp những người trợ giúp xác nhận và có thể hữu ích khi xem xét tham chiếu để xem những câu nhận định và trình trợ giúp nào có thể phù hợp với mã của bạn.

Xác nhận thông thạo và BDD

Một số nhà phát triển thích kiểu xác nhận có thể được gọi là dựa trên hành vi phát triển (BDD) hoặc Kiểu Thông thạo xác nhận. Đây cũng được gọi là "mong đợi" vì điểm truy cập đến việc kiểm tra kỳ vọng là một phương thức có tên expect().

Hy vọng trình trợ giúp hoạt động giống như câu nhận định được viết là phương thức đơn giản các lệnh gọi như assert.ok hoặc assert.strictDeepEquals, nhưng một số nhà phát triển thấy chúng dễ đọc hơn. Xác nhận BDD có thể có dạng như sau:

// 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');

Các kiểu xác nhận này hoạt động được nhờ một kỹ thuật có tên là tạo chuỗi phương thức, trong đó đối tượng do expect trả về có thể liên tục xâu chuỗi với nhau với các lệnh gọi phương thức khác. Một số phần của cuộc gọi, bao gồm cả to.bethat.does ở ví dụ trước, không có chức năng và chỉ được đưa vào để thực hiện lệnh gọi dễ đọc hơn và có thể tạo ra nhận xét tự động nếu kiểm tra không thành công. (Đáng chú ý là expect thường không hỗ trợ ghi chú không bắt buộc, vì chuỗi phải mô tả lỗi một cách rõ ràng.)

Nhiều khung kiểm thử hỗ trợ cả Fluent/BDD và câu nhận định thông thường. Thử nghiệm, ví dụ: xuất cả hai Cách tiếp cận của Chai cũng như cách tiếp cận BDD riêng súc tích hơn một chút. Jest, mặt khác, chỉ bao gồm kỳ vọng theo mặc định.

Nhóm các bài kiểm thử trên các tệp

Khi viết mã kiểm thử, chúng ta thường cung cấp các nhóm ngầm ẩn – thay vì cung cấp các nhóm đó so với tất cả các chương trình kiểm thử nằm trong một tệp, nên bạn nên viết mã kiểm thử trên nhiều tệp tệp. Trên thực tế, trình chạy kiểm thử thường chỉ biết rằng một tệp là để kiểm thử vì của một bộ lọc hoặc biểu thức chính quy được xác định trước, ví dụ: bao gồm tất cả các tệp trong dự án kết thúc bằng đuôi như ".test.jsx" hoặc ".spec.ts" (".test" và ".spec" cùng một số tiện ích hợp lệ).

Các chương trình kiểm thử thành phần thường nằm trong một tệp ngang hàng cho thành phần đang được kiểm thử, như trong cấu trúc thư mục sau:

Danh sách các tệp trong một
  bao gồm UserList.tsx và UserList.test.tsx.
Tệp thành phần và tệp thử nghiệm có liên quan.

Tương tự, các bài kiểm thử đơn vị có xu hướng được đặt bên cạnh mã đang được kiểm thử. Mỗi chương trình kiểm thử toàn diện có thể nằm trong một tệp riêng và thậm chí kiểm thử tích hợp có thể được đặt trong các thư mục riêng của chúng. Các cấu trúc này có thể hữu ích khi các trường hợp kiểm thử phức tạp sẽ phát triển để yêu cầu có các tệp hỗ trợ không kiểm thử riêng, chẳng hạn như các thư viện hỗ trợ cần thiết cho hoạt động kiểm thử.

Nhóm các bài kiểm thử trong tệp

Như được sử dụng trong các ví dụ trước, thông thường bạn nên đặt các kiểm thử bên trong lệnh gọi đến suite() nhóm các chương trình kiểm thử bạn thiết lập bằng test(). Phòng suite thường không phải lúc nào bản thân các bài kiểm thử, mà chúng giúp cung cấp cấu trúc bằng cách nhóm các bài kiểm thử có liên quan hoặc mục tiêu bằng cách gọi phương thức đã thông qua. Đối với test(), phương thức đã truyền mô tả các hành động của chính bài kiểm thử.

Như với lời khẳng định, có một tương đương khá chuẩn trong Fluent/BDD với các thử nghiệm nhóm. Bạn có thể so sánh một số ví dụ điển hình trong đoạn mã sau:

// 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);
  });
})

Trong hầu hết các khung, suitedescribe hoạt động tương tự nhau, tương tự như testit, trái ngược với những khác biệt lớn hơn giữa việc sử dụng expectassert để viết câu nhận định.

Các công cụ khác có phương pháp sắp xếp các bộ công cụ và chương trình kiểm thử khác nhau một cách tinh tế. Cho ví dụ: trình chạy kiểm thử tích hợp của Node.js hỗ trợ các lệnh gọi lồng nhau đến test() để tạo một hệ phân cấp kiểm thử ngầm. Tuy nhiên, Vitest chỉ cho phép loại lồng nhau bằng suite() và sẽ không chạy test() được xác định bên trong một test() khác.

Cũng giống như câu nhận định, hãy nhớ rằng sự kết hợp chính xác của việc nhóm các phương thức mà bộ phần mềm cơ sở của bạn cung cấp không quan trọng. Khoá học này sẽ giúp bạn nắm được nội dung của các nguyên tắc đó mang tính tóm tắt, nhưng bạn sẽ cần tìm hiểu cách chúng áp dụng cho lựa chọn công cụ.

Phương thức vòng đời

Một lý do để nhóm các kiểm thử của bạn, thậm chí là ngầm ẩn ở cấp cao nhất trong một cung cấp phương thức thiết lập và chia nhỏ chạy cho mỗi lần kiểm thử, hoặc cho một nhóm các bài kiểm thử. Hầu hết các khung đều cung cấp 4 phương thức:

Đối với mọi "test()" hoặc "it()" Một lần cho bộ
Trước khi chạy kiểm thử "beforeEach()" `beforeAll()`
Sau lần chạy thử nghiệm "afterEach()" "afterAll()"

Ví dụ: bạn có thể muốn điền sẵn cơ sở dữ liệu người dùng ảo trước mỗi kiểm thử và xoá sau đó:

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

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

Việc này có thể giúp đơn giản hoá quá trình kiểm thử. Bạn có thể chia sẻ cách thiết lập phổ biến và thay vì lặp lại mã trong mỗi lần kiểm thử. Ngoài ra, nếu việc thiết lập và chia nhỏ mã sẽ tạo ra lỗi, đây có thể là dấu hiệu cho thấy các cấu trúc không liên quan đến các bài kiểm thử không thành công.

Lời khuyên chung

Dưới đây là một vài mẹo cần nhớ khi xem xét các dữ liệu gốc này.

Nguyên gốc là một hướng dẫn

Hãy nhớ rằng các công cụ và dữ liệu gốc ở đây và trong các trang tiếp theo sẽ không khớp chính xác với Vitest, Jest, Mocha hoặc Web Test Runner hoặc bất kỳ công cụ nào khác khung cụ thể. Mặc dù chúng tôi đã sử dụng Vitest như một hướng dẫn chung, hãy nhớ ánh xạ tuỳ theo khung mà bạn chọn.

Kết hợp và so khớp câu nhận định nếu cần

Về cơ bản, kiểm thử là mã có thể gửi lỗi. Mỗi người chạy sẽ cung cấp một sơ khai, có thể là test(), để mô tả các trường hợp kiểm thử riêng biệt.

Nhưng nếu trình chạy đó cũng cung cấp assert(), expect() và trình trợ giúp câu nhận định, hãy nhớ rằng phần này tập trung vào sự thuận tiện và bạn có thể bỏ qua nếu bạn cần thiết. Bạn có thể chạy bất kỳ mã nào có khả năng gây ra lỗi, bao gồm cả các mã khác hoặc câu lệnh if kiểu cũ.

Thiết lập IDE có thể là một pha cứu nguy

Đảm bảo rằng IDE của bạn, chẳng hạn như VSCode, có quyền truy cập vào tính năng tự động hoàn thành và về công cụ kiểm thử bạn chọn có thể giúp bạn làm việc hiệu quả hơn. Cho ví dụ: có hơn 100 phương thức trên assert trong câu nhận định Chai thư viện và có tài liệu về bên phải xuất hiện cùng dòng có thể thuận tiện.

Điều này có thể đặc biệt quan trọng đối với một số khung kiểm thử điền sẵn toàn bộ vùng chứa tên với các phương pháp kiểm thử của chúng. Đây là một khác biệt nhỏ, nhưng bạn thường có thể sử dụng thư viện kiểm thử mà không cần nhập các thư viện đó nếu các thư viện đó tự động được thêm vào không gian tên chung:

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

Bạn nên nhập trình trợ giúp ngay cả khi các trình trợ giúp đó được hỗ trợ tự động, vì điều đó giúp IDE có một cách rõ ràng để tra cứu những phương thức này. (Bạn có thể có đã gặp phải vấn đề này khi xây dựng React, vì một số cơ sở mã có khả năng React tệp chung, nhưng một số tệp thì không, nên bạn phải nhập tệp này vào tất cả các tệp sử dụng React.)

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