Kiểm thử là gì

Khi viết phần mềm, bạn có thể xác nhận rằng phần mềm hoạt động đúng cách thông qua kiểm thử. Kiểm thử có thể được định nghĩa rộng là quá trình chạy phần mềm theo các cách cụ thể để đảm bảo phần mềm hoạt động như dự kiến.

Việc kiểm thử thành công có thể giúp bạn tự tin rằng khi bạn thêm mã, tính năng mới hoặc thậm chí là nâng cấp các phần phụ thuộc, phần mềm bạn đã viết sẽ tiếp tục hoạt động theo cách mà bạn mong đợi. Việc kiểm thử cũng có thể giúp bảo vệ phần mềm của bạn khỏi các tình huống hiếm gặp hoặc dữ liệu đầu vào không mong muốn.

Một số ví dụ về hành vi trên web mà bạn có thể muốn kiểm tra bao gồm:

  • Đảm bảo rằng tính năng của trang web hoạt động chính xác khi người dùng nhấp vào một nút.
  • Xác nhận rằng một hàm phức tạp tạo ra kết quả chính xác.
  • Hoàn thành một hành động yêu cầu người dùng đăng nhập.
  • Kiểm tra xem biểu mẫu có báo cáo chính xác lỗi khi nhập dữ liệu không đúng định dạng.
  • Đảm bảo một ứng dụng web phức tạp tiếp tục hoạt động khi người dùng có băng thông cực thấp hoặc không có kết nối mạng.

Kiểm thử tự động so với kiểm thử thủ công

Bạn có thể kiểm thử phần mềm theo hai cách chung: kiểm thử tự động và kiểm thử thủ công.

Kiểm thử thủ công bao gồm việc con người chạy phần mềm trực tiếp, chẳng hạn như tải một trang web trong trình duyệt và xác nhận rằng trang web đó hoạt động như dự kiến. Việc tạo hoặc xác định quy trình kiểm thử thủ công rất đơn giản. Ví dụ: trang web của bạn có tải được không? Bạn có thể thực hiện những thao tác này không? nhưng mỗi lần chạy qua lại tiêu tốn một lượng thời gian rất lớn của con người. Mặc dù con người rất sáng tạo và có thể kích hoạt một loại kiểm thử gọi là kiểm thử khám phá, nhưng chúng ta vẫn có thể kém khả năng nhận biết được các lỗi hoặc sự không nhất quán, đặc biệt là khi thực hiện cùng một tác vụ nhiều lần.

Kiểm thử tự động là bất kỳ quy trình nào cho phép máy tính mã hoá và chạy nhiều lần kiểm thử để xác nhận hành vi dự định của phần mềm mà không nhờ con người thực hiện bất kỳ bước nào lặp lại, chẳng hạn như thiết lập hoặc kiểm tra kết quả. Quan trọng là sau khi bạn định cấu hình kiểm thử tự động, nó có thể chạy thường xuyên. Đây vẫn là định nghĩa rất rộng và đáng chú ý là kiểm thử tự động có mọi loại hình dạng và dạng thức. Phần lớn nội dung của khoá học này liên quan đến việc kiểm thử tự động.

Việc kiểm thử thủ công có vai trò quan trọng, thường là tiền đề của việc viết kiểm thử tự động, nhưng cũng có khi việc kiểm thử tự động trở nên quá không đáng tin cậy, có phạm vi rộng hoặc khó viết.

Nguyên tắc cơ bản thông qua ví dụ

Đối với chúng tôi, là nhà phát triển web viết JavaScript hoặc các ngôn ngữ có liên quan, quy trình kiểm thử tự động ngắn gọn có thể là một tập lệnh giống như tập lệnh này mà bạn chạy mỗi ngày, có thể là thông qua Nút hoặc bằng cách tải tập lệnh đó trong trình duyệt:

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

Đây là ví dụ đơn giản cung cấp các thông tin chi tiết sau:

  • Đây là quá trình kiểm thử vì nó chạy một số phần mềm (hàm Fibonacci) và đảm bảo hành vi của phần mềm này hoạt động theo cách mong muốn bằng cách kiểm tra kết quả so với các giá trị dự kiến. Nếu hành vi không chính xác, lỗi này sẽ gây ra lỗi mà JavaScript biểu thị bằng cách gửi một Error.

  • Mặc dù bạn có thể đang chạy tập lệnh này theo cách thủ công trong thiết bị đầu cuối hoặc trình duyệt, nhưng đây vẫn là một quy trình kiểm thử tự động vì tập lệnh này có thể chạy nhiều lần mà bạn không cần phải thực hiện bất kỳ bước riêng nào. Trang tiếp theo, nơi chạy quy trình kiểm thử, sẽ giải thích thêm.

  • Mặc dù chương trình kiểm thử này không sử dụng bất kỳ thư viện nào – JavaScript có thể chạy ở mọi nơi – đó vẫn là một chương trình kiểm thử. Có nhiều công cụ có thể giúp bạn viết kiểm thử, bao gồm cả những công cụ sẽ được đề cập sau trong khoá học này. Tuy nhiên, tất cả chúng vẫn hoạt động theo nguyên tắc cơ bản là gây ra lỗi nếu xảy ra sự cố.

Kiểm thử thư viện trong thực tế

Hầu hết các thư viện hoặc khung kiểm thử tích hợp sẵn đều cung cấp hai dữ liệu gốc chính giúp việc viết kiểm thử dễ dàng hơn: xác nhận và một cách xác định các kiểm thử độc lập. Những câu lệnh này sẽ được đề cập chi tiết trong phần tiếp theo, xác định và các dữ liệu gốc khác. Tuy nhiên, ở cấp độ cao, điều quan trọng cần nhớ là gần như tất cả các chương trình kiểm thử mà bạn thấy hoặc viết sẽ sử dụng các loại dữ liệu nguyên gốc này.

Xác nhận là một cách kết hợp việc kiểm tra một kết quả và gây ra lỗi nếu xảy ra sự cố. Ví dụ: bạn có thể làm cho bài kiểm thử trước ngắn gọn hơn bằng cách đưa ra 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");

Bạn có thể cải thiện bài kiểm thử này hơn nữa bằng cách xác định các kiểm thử độc lập, được nhóm thành các bộ (không bắt buộc). Bộ công cụ sau đây kiểm thử độc lập hàm Fibonacci và hàm Catalan:

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

Trong ngữ cảnh kiểm thử phần mềm này, kiểm thử dưới dạng một danh từ đề cập đến một trường hợp kiểm thử: một tình huống duy nhất, độc lập và có thể giải quyết, chẳng hạn như trường hợp kiểm thử "mối quan hệ giữa các trình tự" trong ví dụ trước.

Các bài kiểm thử được đặt tên riêng lẻ sẽ hữu ích cho các tác vụ sau, ngoài những tác vụ khác:

  • Là cách xác định mức độ thành công hoặc thất bại của một lượt kiểm thử theo thời gian.
  • Làm nổi bật lỗi hoặc tình huống theo tên để bạn có thể dễ dàng kiểm thử xem tình huống đã được giải quyết hay chưa.
  • Chạy một số thử nghiệm độc lập với các thử nghiệm khác, chẳng hạn như qua bộ lọc toàn cầu.

Có một cách để suy nghĩ về các trường hợp kiểm thử là sử dụng "ba A" của kiểm thử đơn vị: sắp xếp, hành động và xác nhận. Về cơ bản, mỗi trường hợp kiểm thử sẽ:

  • Sắp xếp một số giá trị hoặc trạng thái (điều này có thể chỉ là dữ liệu đầu vào được mã hoá cứng).
  • Thực hiện một hành động, chẳng hạn như gọi một phương thức.
  • Xác nhận các giá trị đầu ra hoặc trạng thái đã cập nhật (sử dụng assert).

Quy mô kiểm thử

Các mã mẫu trong phần trước mô tả một kiểm thử đơn vị, vì chúng kiểm thử các phần nhỏ của phần mềm, thường tập trung vào một tệp duy nhất và trong trường hợp này, chỉ là kết quả của một hàm duy nhất. Độ phức tạp của quá trình kiểm thử tăng lên khi bạn xem xét mã từ nhiều tệp, thành phần hoặc thậm chí là nhiều hệ thống được kết nối với nhau (đôi khi nằm ngoài tầm kiểm soát của bạn, chẳng hạn như dịch vụ mạng hoặc hành vi của một phần phụ thuộc bên ngoài). Do đó, các loại kiểm thử thường được đặt tên dựa trên phạm vi hoặc tỷ lệ của chúng.

Cùng với kiểm thử đơn vị, một số ví dụ về các loại kiểm thử khác bao gồm kiểm thử thành phần, kiểm thử trực quankiểm thử tích hợp. Không có tên nào trong số này có định nghĩa nghiêm ngặt và có thể có ý nghĩa khác nhau tuỳ thuộc vào cơ sở mã của bạn. Vì vậy, hãy nhớ sử dụng các tên này làm hướng dẫn và tự nghĩ ra các định nghĩa phù hợp với bạn. Ví dụ: một thành phần đang được kiểm thử trong hệ thống của bạn là gì? Đối với nhà phát triển Phản ứng, thuộc tính này có thể ánh xạ theo nghĩa đen đến một "Thành phần phản ứng", nhưng cũng có thể có ý nghĩa khác với nhà phát triển trong các ngữ cảnh khác.

Quy mô của một kiểm thử riêng lẻ có thể đặt kiểm thử đó bên trong một khái niệm thường được gọi là "kim tự kiểm thử". Đây có thể là một quy tắc chung về nội dung kiểm thử và cách chạy kiểm thử.

Kim tự tháp kiểm thử, với các kiểm thử toàn diện (E2E) ở trên cùng, các kiểm thử tích hợp ở giữa và các kiểm thử đơn vị ở dưới cùng.
Kim tự tháp thử nghiệm.

Ý tưởng này đã được lặp lại và nhiều hình dạng khác hiện đã trở nên phổ biến, chẳng hạn như hình thoi thử nghiệm hoặc hình nón băng thử nghiệm. Các mức độ ưu tiên viết kiểm thử của bạn có thể là riêng biệt đối với cơ sở mã của bạn. Tuy nhiên, một đặc điểm phổ biến là các quy trình kiểm thử đơn giản hơn (như kiểm thử đơn vị) có thể chạy nhanh hơn, dễ viết hơn (vì vậy bạn sẽ có nhiều kiểm thử hơn) và kiểm thử trong phạm vi giới hạn, trong khi các quy trình kiểm thử phức tạp như kiểm thử toàn diện khó viết nhưng có thể kiểm thử phạm vi rộng hơn. Trên thực tế, lớp trên cùng của nhiều "hình dạng" kiểm thử có xu hướng là kiểm thử thủ công, vì một số tương tác của người dùng quá phức tạp nên không thể mã hoá thành kiểm thử tự động.

Các loại này sẽ được mở rộng trong các loại kiểm thử tự động.

Kiểm tra kiến thức

Hầu hết các thư viện và khung kiểm thử cung cấp những dữ liệu gốc nào?

Dịch vụ chạy sử dụng nhà cung cấp dịch vụ đám mây.
Một số trình chạy dựa trên trình duyệt cung cấp cho bạn một cách để thuê ngoài các kiểm thử của bạn, nhưng đó không phải là một tính năng bình thường của các thư viện kiểm thử.
Các xác nhận gây ra trường hợp ngoại lệ nếu chúng không được thoả mãn.
Mặc dù bạn có thể gửi lỗi để không vượt qua bài kiểm thử, nhưng assert() và các biến thể của nó thường được đưa vào vì chúng giúp quá trình viết kiểm thử trở nên dễ dàng hơn.
Cách để phân loại các kiểm thử vào kim tự tháp kiểm thử.
Không có một cách chuẩn nào để làm việc này. Bạn có thể thêm tiền tố tên các chương trình kiểm thử hoặc đặt các chương trình kiểm thử đó vào các tệp khác nhau, nhưng việc phân loại không thực sự được tích hợp vào hầu hết các khung kiểm thử.
Có thể xác định các kiểm thử độc lập theo hàm.
Phương thức test() có trong hầu hết mọi trình chạy kiểm thử. Điều này rất quan trọng vì mã kiểm thử không chạy ở cấp cao nhất của một tệp, điều này cho phép trình chạy kiểm thử coi mỗi trường hợp kiểm thử là một đơn vị độc lập.