Kiểm thử thủ công hay tự động: khi nào dùng cách nào?

Test tay và test tự động làm hai việc khác nhau. Khi nào nên dùng cách nào, khi nào tự động hóa mới đáng đầu tư, và những sai lầm khiến nó thành lãng phí.

Gần như dự án nào khi bắt đầu cũng có người hỏi: “Mình nên test tay hay viết test tự động?” Nghe như phải chọn một trong hai, nhưng thật ra không phải vậy. Hai cách này làm hai việc khác nhau. Test tự động lặp lại những bước kiểm tra mà bạn đã biết là cần, lần nào cũng như lần nào, không bao giờ chán. Còn một người test tay thì tìm ra những chỗ chưa ai nghĩ tới để kiểm tra.

Vậy câu hỏi đúng là: test nào nên làm tay, test nào nên tự động hóa. Dưới đây là cách chúng tôi quyết định.

Test tay mạnh ở đâu?

Test tay là một người dùng thử phần mềm với mục tiêu rõ ràng và quan sát kỹ. Nên chọn cách này khi:

  • Tính năng còn mới hoặc đang thay đổi. Thiết kế và yêu cầu còn đổi hằng tuần thì script viết hôm nay mai đã lỗi thời. Cứ test tay cho đến khi tính năng ổn định.
  • Cần con người đánh giá. “Màn hình này có khó hiểu không?”, “Thông báo lỗi này người dùng có hiểu không?”, “Giao diện trên điện thoại này có bị vỡ không?” Script không trả lời tốt những câu như vậy.
  • Cần khám phá. Exploratory testing là cố tình “phá” sản phẩm: nhập dữ liệu lạ, làm các bước theo thứ tự bất thường, mở hai tab cùng lúc. Đây là cách tìm ra những bug mà không test case nào viết sẵn.
  • Chỉ kiểm tra một lần. Một đợt chuyển dữ liệu, hay một màn hình admin hiếm khi dùng, thường không đáng để tự động hóa.

Test tự động mạnh ở đâu?

Test tự động là code điều khiển app và kiểm tra kết quả. Nên chọn cách này khi:

  • Cùng một bước kiểm tra phải chạy đi chạy lại. Regression test, tức là kiểm tra những gì chạy đúng ở bản trước vẫn còn đúng, là ví dụ điển hình.
  • Đó là luồng quan trọng. Đăng ký, đăng nhập, đặt hàng, thanh toán phải được kiểm tra sau mỗi lần thay đổi, chứ không phải lúc nào rảnh mới test.
  • Có quá nhiều tổ hợp. Mười mã giảm giá nhân ba loại khách hàng là ba mươi trường hợp: test tay thì cực, viết vòng lặp thì vài dòng.
  • Phải chạy ở mỗi commit. Trong CI, test chạy trước khi deploy, nên một thay đổi làm hỏng app sẽ không bao giờ đến tay người dùng.

Quy tắc đơn giản để quyết định

Chỉ tự động hóa một test khi nó thỏa cả ba điều kiện:

  1. Lặp lại thường xuyên, ít nhất là mỗi lần release.
  2. Ổn định: tính năng không sắp bị làm lại ở sprint sau.
  3. Kết quả rõ ràng đúng hoặc sai: tổng tiền đúng hay sai, trang mở được hay không.

Thiếu một trong ba thì cứ test tay trước, đợi tính năng ổn định rồi tính tiếp.

Những sai lầm khiến tự động hóa thành lãng phí

Tự động hóa quá sớm. Script viết cho một màn hình còn đang thay đổi sẽ hỏng sau mỗi lần sửa giao diện. Cuối cùng cả team ngồi sửa test thay vì đi tìm bug.

Cái gì cũng test qua UI. Test end-to-end bấm qua trình duyệt là loại chậm nhất và dễ hỏng nhất. Phần lớn việc kiểm tra nên nằm ở tầng thấp hơn: unit test cho logic nghiệp vụ, API test cho server, và chỉ một số ít test end-to-end cho các luồng quan trọng. Đó là “kim tự tháp kiểm thử”: đáy rộng gồm nhiều test nhanh, đỉnh hẹp gồm vài test chậm.

Sống chung với test chập chờn. Một test lúc pass lúc fail không rõ lý do sẽ dạy cả team phớt lờ build đỏ. Sửa nó, hoặc xóa đi. Một bộ test không ai tin còn tệ hơn không có test.

Bỏ hẳn test tay. Bộ test tự động chỉ kiểm tra những gì người viết nghĩ ra lúc viết. Không có exploratory testing thì những bug chưa ai nghĩ tới sẽ đi thẳng lên production.

Bài toán chi phí

Lần đầu, một test tự động tốn công hơn: phải có người viết, và phải bảo trì khi sản phẩm thay đổi. Bù lại, mỗi lần chạy là một lần hoàn vốn. Nên tính toán phụ thuộc vào số lần chạy. Một bước kiểm tra mỗi năm chỉ cần một hai lần thì test tay rẻ hơn. Còn nếu lần release nào cũng phải chạy, trên nhiều trình duyệt, thì tự động hóa rẻ hơn rất nhiều.

Một ví dụ rất nên tự động hóa: quy tắc tính giá cần kiểm tra với nhiều dữ liệu khác nhau, mà chẳng ai muốn gõ lại trước mỗi lần release.

import { expect, test } from '@playwright/test';

const cases = [
  { code: 'WELCOME10', subtotal: 200_000, total: 180_000 },
  { code: 'FREESHIP', subtotal: 200_000, total: 200_000 },
  { code: 'EXPIRED', subtotal: 200_000, total: 200_000 },
];

for (const { code, subtotal, total } of cases) {
  test(`discount ${code} on ${subtotal}`, async ({ request }) => {
    const response = await request.post('/api/cart/quote', { data: { code, subtotal } });
    expect((await response.json()).total).toBe(total);
  });
}

Test này gọi thẳng API chứ không đi qua trình duyệt, nên chạy nhanh và không hỏng khi trang thanh toán đổi giao diện.

Chúng tôi kết hợp hai cách thế nào trong một lần release

Thứ tự quen thuộc của chúng tôi:

  1. Phân tích nghiệp vụ, thống nhất các luồng quan trọng.
  2. Viết test case.
  3. Test từng tính năng: test tay khi còn mới, tự động hóa khi đã ổn định.
  4. Chạy bộ regression test tự động.
  5. Exploratory testing bằng tay, tìm những gì test case bỏ sót.
  6. Test tích hợp cho bản release, rồi UAT cùng khách hàng.
  7. Kiểm tra trên production ngay sau khi deploy.

Tự động hóa gánh phần lặp lại ở bước 3, 4 và 6. Con người lo phần cần suy nghĩ ở bước 1, 5 và 7.

Chọn công cụ nào?

Với tự động hóa trên trình duyệt, ba lựa chọn phổ biến là:

  • Playwright: một API chạy được trên Chromium, Firefox và WebKit (engine của Safari), có sẵn cơ chế chờ và chạy song song. Lựa chọn mặc định tốt cho dự án mới.
  • Cypress: trình chạy test rất dễ dùng cho team front-end, viết test tới đâu thấy kết quả tới đó. Hỗ trợ Safari còn hạn chế.
  • Selenium: lâu đời nhất, dựa trên chuẩn WebDriver, hỗ trợ nhiều ngôn ngữ nhất. Hợp với team đã có sẵn bộ test Java hoặc C# và hạ tầng test grid.

Công cụ không quan trọng bằng những quyết định ở trên. Một bộ test được chọn lọc kỹ, viết bằng công cụ nào trong ba cái cũng đều tốt hơn một bộ test to mà chập chờn viết bằng công cụ “xịn nhất”. Chúng tôi dùng được cả ba, và thường giữ nguyên công cụ team đang dùng.

Tóm lại

  • Test tay và test tự động không loại trừ nhau, mỗi cách làm một việc.
  • Test tay khi tính năng còn mới, đang thay đổi, cần con người đánh giá, hoặc chỉ kiểm tra một lần.
  • Tự động hóa những gì lặp lại, ổn định và có kết quả rõ ràng, bắt đầu từ các luồng quan trọng.
  • Đặt phần lớn test tự động ở dưới tầng UI, và sửa hoặc xóa test chập chờn.
  • Luôn dành thời gian cho exploratory testing.

Sắp ra mắt một app xây bằng công cụ AI? Xem thêm cách kiểm thử ứng dụng xây bằng AI trước khi ra mắt.

Sắp release phiên bản mới?

Liên hệ ngay