Trong bài viết này tôi xin chia sẻ 9 tư duy (mindset) tâm đắc mà tôi đúc kết được sau 5 năm cùng xây dựng tự động hóa kiểm thử (TĐHKT) tại Cybozu Vietnam. Qua bài viết này tôi hy vọng nó có thể giúp ích cho những người mới tiếp cận automation testing có thêm một bài tham khảo. Từ đó, có thể triển khai dự án automation testing thuận lợi và giàu tính maintainable mà không phải “đập bỏ làm lại từ đầu” hoặc phải nhặt quá nhiều sạn khi chuyển sang maintain.
Hơi sớm, nhưng suy cho cùng thì automation testing cũng là một dự án phần mềm do đó sau implementation (thực hiện) là giai đoạn maintain.
Bên dưới là 9 tư duy tôi muốn chia sẻ:
1. Automation testing code là code “thật”
Hiện nay, rất nhiều tài liệu được đầu tư rất bài bản và nhiều community như stackoverflow với những code snippet nên không khó để có thể implement một test-case/scenario chạy được với các yêu cầu đặt ra. Từ sự dễ dàng này đôi khi làm chúng ta quên mất cần phải đầu tư một cách có quy chuẩn dẫn đến những khó khăn về sau. Do đó, tư duy đầu tiên tôi xin đề cập đó là:
Automation testing code là code “thật”
Với tư duy này muốn truyền đạt tới chúng ta khi khi viết source code cho TĐHKT nói riêng chúng ta đầu tư một cách nghiêm túc ngay từ đầu nó sẽ mang lại những lợi ích sau:
- Test code dễ dàng đọc hiểu với bất kỳ ai; ngay cả chính mình sau 3 tháng hay 3 năm mà không lo lắng như: “Nó là cái quái gì vậy?”
- Dễ dàng phát hiện bug tiềm ẩn trong source code của chương trình lúc implement và review
- Dễ dàng tái hiện bug từ failure report
- Dễ dàng mở rộng dự án theo thời gian như: Từ test case đến loại test
- Giúp chia sẻ và đồng bộ kiến thức giữa các thành viên trong team mà không cần thông qua chia sẻ nội bộ tốn kém
- Giảm thời gian đào tạo thành viên mới vì code đã đạt được self-document code
Ví dụ: Code được implement cho test case: Adding successful a Shared Address với tư duy “là code thật” cho Garoon's Address bằng tài khoản login name: sato
và password sato
)
import { AddingAddressBookFlow } from '#e2e-core/address/test-flows/book';
import testData from './address-book-shared.data';
import { Authenticating} from '#e2e-core/other/test-flows';
describe('Address Book Shared', () => {
it('should be added a Shared Address successful', () => {
new Authenticating(testData.adminCredential)
.login()
.verifyLoggedInSuccessfully();
new AddingAddressBookFlow(testData.sharedAddressBookInfo)
.addBook()
.verifyBookDetails();
}, WdioConfig.retryNumber());
});
Ví dụ: Code được implement với suy nghĩ “chỉ cần chạy được rồi tính sau”
import AddingAddressBookFlow from '#e2e-core/address/test-flows/book/AddingBook';
import testData from './address-book-shared.data';
import Authenticating from '#e2e-core/other/test_flows/login/Authenticating';
import ElementUtils from '#e2e-core/element/ElementUtils';
describe('Address Book Shared', () => {
it('GRacceptance85_001', () => {// 1. Mô tả không giúp con người hiểu được
const ad = {};
ad.bookName = 'Book Name ABC'; // 2. Dữ liệu cứng nhắc, hệ thống không cho nhập trùng dữ liệu như vậy developer rất cực phải đổi giá trị mới khi chạy
ad.bookCode = 'code-12345';
new Authenticating(testData.adminCredential).login(); // 3. Phó thác đăng nhập thành công hay thất bại cho may rủi là không viết thêm assertion logic
browser.url('g/address/system/book_list'); // 4. Logic mang tính cục bộ hóa test case, tức là hành vi truy cập này không thể dùng ở test case khác trong cùng testsuite
ElementUtils.element('//*[@id="system_shared_address_book_add"]').click();
ElementUtils.element('//*[@id="sharedbookName-label-line-value-def"]').setValue(ad.bookName);
ElementUtils.element('//input[@name="id"]').setValue(ad.bookCode);
ElementUtils.element('.margin').click(); // 5. Tối nghĩa, vì không biết là click vào đối tượng gì trên trang
const adding = new AddingAddressBookFlow(ad);
adding.verifyBookDetails();
}); // 6. Không triển khai logic cho trường hợp mạng không ổn định sau một lần chạy vì lý do developer không kiểm soát
});
Ở Cybozu Vietnam, tư duy này được hiện thực với một quy trình thống nhất như sau:
- Bước 1: Giải pháp khả thi nhất được đề cử từ người phụ trách implement (cá nhân tự đưa ra hoặc tham khảo ý kiến từ đồng đội)
- Bước 2: Implement dựa trên giải pháp đã chọn ở bước 1
- Bước 3: Tự review source-code đã triển khai: Nó đáp ứng yêu cầu đưa ra và bộ tiêu chuẩn code của Cybozu coding standard
- Bước 4: Review source code chéo(source code được một thành viên khác trong team review một lần nữa)
- Bước 5: Hiệu chỉnh source code nếu có ở Bước 4
- Bước 6: Hợp nhất(merged) source code chỉnh sửa sau review phản hồi vào codebase và được quản lý phiên bản (version control) trong suốt vòng đời sản phẩm triển khai kiểm thử tự động.
2. Automation testing không thể thay thế hoàn toàn manual testing
Cách đây 4 năm, khi tôi mới đảm nhiệm vai trò cùng phát triển hệ thống TĐHKT tôi tự đặt ra câu hỏi “Liệu TĐHKT có thay thế manual testing?”. Có những lúc “quá khích:)” cho rằng một ngày không xa TĐHKT sẽ thay thế manual test hoàn toàn :).
Giờ khi viết bài này tháng năm lăn xả vào TĐHKT ở công ty thì tôi từng trả lời câu hỏi này ở workshop do Topdev tổ chức rằng:
KHÔNG bao giờ TĐHKT thay thế manual testing ít nhất là 10 năm tới. Vì sao?
- Chúng ta vẫn cần manual testing cho những tính năng mới được thêm vào software product.
Vì chúng ta rất khó hoàn thành song song testing code và production code dù áp dụng mô hình phát triển là gì, kể cả DevOps - Vẫn cần manual testing cho những loại test như: performance testing, security testing.
Vì hiện tại chưa thể triển khai TĐHKT một cách hoàn chỉnh và tin cậy cho những loại test này. - Cần có manual test để training về Kiểm thử phần mềm trước khi đi vào tự động hoá :)
3. Để đạt mức chuyên gia trong TĐHKT chúng ta cần nỗ lực nhiều
Là khiếm khuyết trong nhận định nếu chúng ta nghĩ rằng làm TĐHKT là chỉ viết test code và thực hiện phép ánh xạ từ manual testing sang automation testing code.
Theo tôi TĐHKT là một lĩnh vực rộng lớn, đòi hỏi chúng ta nhiều kỹ năng cứng lẫn kỹ năng mềm. Hơn nữa chúng ta liên tục cập nhật những công nghệ và kỹ thuật mới back-end đến front-end. Dưới đây, tôi xin liệt kê những phần kiến thức mà một người làm TĐHKT phần lớn sẽ trải qua:
- Implement kịch bản test bao gồm phải áp dụng principle, design-pattern, và tuân thủ coding standard
- Có kiến thức về software architecture, software structure để build project quy mô (gồm nhiều loại test trong hệ thống, unit-test, end-to-end testing,…)
- Kiến thức về ảo hoá (virtualisation) như: Docker và vagrant để triển khai test runner, testing site trên đa nền tảng
- Triển khai và vận hành CI/CD như: CircleCI, Jenkins
- Server để có thể tùy chỉnh các service (dịch vụ) cũng như lib (thư viện) cho hệ thống kiểm thử có thể thực thi trên nó
- Nghiên cứu công nghệ mới, research giải pháp
Vì mỗi công nghệ, framework có những điểm hay riêng của nó. Việc nghiên cứu đôi khi cho ta nhiều cảm hứng để triển khai TĐHKT hay hơn, hiệu quả hơn. - Tổng hợp thông tin/giải pháp để đánh giá tính khả thi, kỹ năng này giúp làm việc nhóm hiệu quả
- Đào tạo chuyển giao về TĐHKT cho những người mới
- Viết tài liệu, để tài liệu hoá lại hệ thống; người mới và người cũ giúp nắm bắt hoặc tham khảo để từ đó mở rộng và cải thiện hệ thống tốt hơn trong quá trình vận hành ít gặp rủi ro.
- Kỹ năng thuyết trình ở các hội nghị chuyên ngành. Khi bạn lĩnh hội kiến thức và trải nghiệm đủ lớn, lúc này bạn trở thành đàn anh, đàn chị và thể hiện một chút trách nhiệm với cộng đồng.
4. Cost luôn có giới hạn do đó phải cân nhắc chọn lựa khi bắt đầu
Trong tập test case mà chúng ta chuẩn bị triển khai TĐHKT, tôi tin rằng mỗi test case có độ ưu tiên khác nhau. Độ ưu tiên đó đôi khi còn phụ thuộc vào "hệ quy chiếu" của mỗi nhóm, công ty và cá nhân.
Chẳng hạn, ở Cybozu Vietnam trong tập test-case, mỗi test-case sẽ có một độ ưu tiên (priority) như sau: S, A, B, C, D.
Với manual S là ưu tiên test nhất và quan trọng nhất, đến đây chắc có bạn đọc sẽ hình dung rằng: khi tự động hoá cũng vậy nhỉ, nên ưu tiên test-case với độ ưu tiên S.
Không! với tôi thì nên tiếp cận theo "chiến lược":
- Test case có độ bao phủ cao nhất (bất chấp là S hay C)
- Nền tảng hiện tại có sẵn nhiều nhất (tức là đã có codebase, components phụ trợ, hay source code đã có sẵn) mà chúng ta tốn ít công sức nhất để hoàn thành.
- Còn nếu xét trên phương diện người sử dụng TĐHKT thì chúng ta ưu tiên implement loại test, test case nào mang lại "giá trị" nhiều nhất (được định lượng qua những điểm bên dưới):
Giảm cost manual testing nhất
Tần suất sử dụng cao loại test này/test case nhất
Triển khai dễ dàng nhất
5. Cố gắng tìm cách triển khai (implement) tốt nhất là lãng phí
Không có cách tốt nhất để implementation do đó chúng ta không cần lãng phí thời gian để đi tìm nó
Theo tôi, source code sau khi implement được gọi là tốt nhất là source code đáp ứng hai tiêu chí:
- Ai cũng có thể đọc hiểu nó làm gì và tái hiện được kịch bản test từ source code (ngay cả người không làm về TĐHKT).
- Và source code có thể debug được
Tôi tin rằng, mỗi đội nhóm, mỗi tổ chức và mỗi ngôn ngữ có tiêu chuẩn và nguyên lý để định lượng một “test script” như thế nào là tốt.
Có lẽ, source code của TĐHKT có chút đặc thù, không phải chỉ dành riêng cho developer, tester mà nó dành cho tất cả mọi người trong nhóm phát triển (QA, Developer, PO, PM,…)
Do đó nếu source code áp dụng principle, design pattern điều này sẽ có chút khó khăn với những người mà không có tình yêu với viết mã mãnh liệt giống dev.
Nên trong tư duy này tôi khuyên các bạn cân nhắc thật kỹ việc viết test code sao cho dễ đọc và dễ tiếp cận nhất. Xin nói thêm, điều này không có nghĩa là tôi phản đối việc áp dụng principle, design pattern.
6. Một giải pháp chỉ giải quyết tốt một bài toán automation testing
Có nhiều giải pháp cho bài toán automation testing đặt ra nhưng trong đó chỉ có một cách phù hợp nhất!
Trong một hệ thống automation testing có nhiều loại test khác nhau như: acceptance testing, regression testing, và APIs (Rest, SOAP,…). Mỗi một loại test có đặc thù riêng nên chúng ta có một giải pháp để implement phù hợp nhất.
Đối với API testing chúng ta sẽ sử dụng giải pháp "data driven testing" thay vì "keyword driven testing". Vì implement này giúp tránh bùng nổ test code. Ví dụ: 100 test data chỉ có một test code.
Còn loại acceptance testing, chúng ta dùng keyword driven testing giúp triển khai dễ dàng
Tư duy này cũng giúp chúng ta sẽ nâng cao kỹ năng hơn vì phải tìm hiểu nhiều phương pháp, frameworks hơn giúp tư duy automation testing tăng lên một cách từ từ và vô tình
7. Mọi nỗ lực đều mang lại kinh nghiệm quý
Kinh nghiệm lập trình, kiến thức lập trình càng nhiều thì kỹ năng càng cao. Việc này đồng nghĩa với việc chúng ta sẽ đưa ra được nhiều giải pháp cho một bài toán test automation cụ thể. Giải pháp chúng ta đưa ra cũng khả thi (có thể triển khai được) với thời gian ngắn nhất.
Hay nói một cách khác, chính nhờ vào những trải nghiệm, những tìm hiểu, những cố gắng trong quá khứ sẽ là điều kiện để tư duy máy tính có cơ hội hoạt động.
Nếu chúng ta không nỗ lực để có kinh nghiệm sẽ không có từ đó chúng ta sẽ rất khó để giải quyết các bài toán có độ phức tạp cao trong test automation. Hoặc chúng ta sẽ khó đảm bảo deadline các task.
Ví dụ: Giải pháp "data driven testing" là phù hợp nhất để test các loại API như rest, SOAP khi triển khai Garoon's e2e. Vì sao tôi đưa ra như vậy? Là vì tôi đã có kiến thức đã tìm hiểu trước đó. Như vậy tôi đưa ra giải pháp tin cậy, có cơ sở và triển khai được. Như vậy nỗ lực để tìm hiểu data driven testing vô tình cho tôi kinh nghiệm quý báo khi bài toán triển khai API test.
8. Automation testing khó phát hiện những lỗi liên quan tới hiển thị, định dạng
automation testing không phải bug nào cũng có thể phát hiện và phát huy tốt nhiệm vụ kiểm thử
Bên dưới tôi xin đề cập tới tình huống mà automation testing khó phát hiện bug liên quan đến hiển thị, định dạng:
- Màn hình đăng nhập thường có ba html elements là username textbox, password textbox, và một Sign-in button.
- Developer viết source-code handle cho 3 đối tượng này thông qua name attribute hoặc CSS selector hay Xpath.
Nên lỗi phát sinh là không nạp được file CSS cho trang này hoặc giao diện bị vỡ thì test-case với kịch bản đăng nhập vẫn hoạt động bình thường mà không report failed. Mặc dù end-user trường hợp này phát hiện là lỗi trong nháy mắt.
Vậy ở trên là một ví dụ để thấy lỗi liên quan tới UI thường tự động hoá khó hoặc rất khó để đạt được sự khả thi. Trong tư duy này theo tôi, những test-case/kịch bản liên quan tới CSS chúng ta không nên tự động hoá vì bản chất implement rất khó mà nó rất dễ để degrade khi maintain.
Xin nói thêm, khó không có nghĩa là không làm được, ngày nay rất nhiều kỹ thuật hỗ trợ và AI, hay OCR tham gia vào quá trình kiểm thử phần mềm do đó kiểm lỗi giao diện là làm được và bạn có thể tìm hiểu thêm trên applitools. Vậy KHÓ mà tôi đề cập ở đây là: tốn nhiều công sức nhưng giá trị mang lại không tương xứng.
9. Không nên bắt đầu dự án automation testing nếu chúng ta chưa thật sự rõ về nghiệp vụ sản phẩm
Một hành động dù lớn hay nhỏ bé cho một sự việc gì đó của chúng ta mà chúng ta chưa rõ về nó thì quả thật tai hại phải không! automation testing cũng không ngoại lệ, do đó biết rõ nghiệp vụ của sản phẩm trước khi triển khai tự động hoá giúp chúng ta những điều sau:
- Triển khai test-case nhanh hơn vì chúng ta đã biết chính xác nghiệp vụ mình làm và đó là kịch bản tốt nhất tại thời điểm triển khai, mà không bị chi phối hoặc dừng lại để suy nghĩ cho những mơ hồ xảy ra.
- Có thể tận dụng lại những code snippet đã có trước đó thông qua nhận dạng tính năng, hoặc ít ra có thể tận dụng lại phương pháp thay vào đó là đi tìm hiểu và đánh giá (công việc tìm hiểu và lựa chọn giải pháp thường tốn 70% thời gian tổng thể của một todo)
- Đưa ra giải pháp phù hợp nhất cho bài toán cần implement vì có sự hiểu biết bao quát và tổng thể, hạn chế việc làm phức tạp hơn khi implement.
Gia tăng thành công của dự án và tăng độ tin cậy, giúp đội nhóm hoạt động hiệu quả hơn.
Một số thuật ngữ sử dụng trong bài viết
implement: triển khai, trong bài viết này với ý nghĩa là viết mã nguồn cho kịch bản tự động hóa