TDD (Test-Driven Development) là mô hình phát triển với trọng tâm hướng về việc kiểm thử. TDD được xây dựng theo hai tiêu chí: Test-First (Kiểm thử trước) và Refactoring (Điều chỉnh mã nguồn)
TDD là một quá trình phát triển lặp đi lặp lại. Mỗi lần lặp lại bắt đầu với một tập hợp các bài kiểm tra được viết cho một phần chức năng mới. Các thử nghiệm này được cho là không thành công trong khi bắt đầu lặp lại vì sẽ không có mã ứng dụng tương ứng với các thử nghiệm.
Vì trong giai đoạn tiếp theo của quá trình lặp lại, Mã ứng dụng được viết với mục đích vượt qua tất cả các bài kiểm tra được viết trước đó trong quá trình lặp lại. Khi mã ứng dụng đã sẵn sàng, các bài kiểm tra sẽ được chạy.
TDD đáp ứng “Tuyên ngôn về Agile” khi bản thân quy trình TDD thúc đẩy tính thực tiễn của sản phẩm, tương tác với người dùng. Để phát huy tối đa những lợi ích mà TDD mang lại, độ lớn của 1 đơn vị tính năng phần mềm (unit of function) cần đủ nhỏ để kịch bản kiểm thử dễ dàng được xây dựng và đọc hiểu, công sức debug kịch bản kiểm thử khi chạy thất bại cũng giảm thiểu hơn.
Theo TDD thì đa phần các test do Developer viết. Tester cũng có khi viết nhưng ít hơn.
Nếu chúng ta tóm tắt điều này dưới dạng các giai đoạn trong quá trình phát triển thì gồm các giai đoạn sau: Xác định yêu cầu, Thực hiện các bài kiểm tra, Sửa/Thêm/Tái cấu trúc mã.
An sẽ mô phỏng các giai đoạn thông qua Code nhé.
Chúng ta sẽ lấy một ví dụ đơn giản về ứng dụng máy tính và chúng ta sẽ xác định các yêu cầu dựa trên các tính năng cơ bản của máy tính. Để đơn giản hơn, chúng ta sẽ cô đọng ứng dụng máy tính thành một lớp java đơn giản:
package com.anhtester;
public class Calculator {
public int add(int number1, int number2) {
return 0;
}
}
Trong giai đoạn 1, các yêu cầu ứng dụng được thu thập và xác định. Lấy ví dụ về một máy tính đơn giản, chúng ta có thể nói rằng trong lần lặp 1 chúng ta muốn thực hiện 3 phép tính là:
Yêu cầu: Máy tính phải có khả năng cộng hai số.
Test 1: Cho hai số dương (10 và 20) máy tính có thể cộng hai số đó lại và cho ta kết quả đúng (30)
Test 2: Cho hai số âm (-10 và -20) máy tính cộng hai số đó lại và cho ta kết quả đúng (-30)
Trong giai đoạn 1, tất cả những gì chúng ta phải làm là viết các bài kiểm tra cho tất cả các yêu cầu tại thời điểm đó.
Yeah như vậy Developer sẽ viết các bài kiểm thử cho chức năng của Calculator theo yêu cầu cộng hai số gồm 2 test cases.
Bây giờ An cũng dùng Java để viết test cases nhé. Sử dụng TestNG Framework để thực hiện Unit Test cho 2 test cases trên.
package com.anhtester;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestCalculator {
private Calculator myCalculator = new Calculator();
@Test
public void testAddTwoPositiveNumbers()
{
int expectedResult = 30;
int actualResult = myCalculator.add(10, 20);
Assert.assertEquals(actualResult, expectedResult, "Sai. Kết quả không chính xác.");
}
@Test
public void testAddTwoNegativeNumbers()
{
int expectedResult = -30;
int actualResult = myCalculator.add(-10, -20);
Assert.assertEquals(actualResult, expectedResult, "Sai. Kết quả không chính xác.");
}
}
Kết quả khi chạy testAddTwoPositiveNumbers:
java.lang.AssertionError: Sai. Kết quả không chính xác.
Expected :30
Actual :0
Như vậy sau khi chạy test case trên cần xem lại phương thức "add" của chương trình máy tính Calculator.
..... một tiếng sau....
Và sau khi Dev xem lại thì phát hiện là nó return 0 nghĩa là nó luôn luôn bằng 0. Sai phải rồi 😄
(lúc đó chắc đang hẹn ghệ đi ăn hơi vội xíu @@)
Tương ứng với sai đó thì Dev cần sửa lại cho đúng với thuần phong mỹ tục 😝
Mã ở đây là cái mã code của ứng dụng phần mềm á nhen. Không phải cái mã code auto test đâu đà @@
Sau khi kiểm tra thất bại ở bước trước, chúng ta vào ứng dụng chỉ cần chỉnh lại phần return kết quả của phương thức "add". Bây giờ lớp Calculator của chúng ta sẽ thế này:
package com.anhtester;
public class Calculator {
public int add(int number1, int number2) {
return (number1 + number2);
}
}
Với thay đổi này, chúng ta sẽ chạy lại Giai đoạn 2 đã đề cập trước đó nghĩa là chạy lại các test cases cho yêu cầu đó sau khi chỉnh sửa lại.
Kết quả của hai bài kiểm tra là:
===============================================
Default Suite
Total tests run: 2, Passes: 2, Failures: 0, Skips: 0
===============================================
![[Cucumber TestNG] Bài 1: Giới thiệu hướng phát triển TDD và BDD trong code Automation Test | Anh Tester](/uploads/lesson/cucumber_testng/tdd_test_cases.jpg)
À pass cả 2 test cases. Yeah như vậy là xong phần Unit Test rồi.
Tương tự viết thêm các test cho các yêu cầu còn lại như ví dụ bên trên.
Khi tất cả các bài kiểm tra vượt qua, nó báo hiệu sự kết thúc của quá trình lặp lại. Nếu có nhiều tính năng hơn cần được triển khai trong sản phẩm của bạn, thì sản phẩm sẽ lại trải qua các giai đoạn tương tự nhưng lần này với bộ tính năng mới và có nhiều thử nghiệm hơn.
Tóm tắt hướng TDD nó thế này:![[Cucumber TestNG] Bài 1: Giới thiệu hướng phát triển TDD và BDD trong code Automation Test | Anh Tester](/uploads/lesson/cucumber_testng/tdd_cycle.jpg)
Tiếp theo, chúng ta sẽ chuyển sang BDD. Phần này sẽ tạo cơ sở để hiểu Gherkin và cuối cùng là Cucumber.
Chúng ta đã thảo luận về cách TDD là một quy trình phát triển tập trung vào thử nghiệm, trong đó chúng ta bắt đầu viết thử nghiệm trước. Ban đầu, các thử nghiệm này không thành công nhưng khi chúng ta thêm nhiều mã ứng dụng hơn, các thử nghiệm này sẽ vượt qua. Điều này giúp chúng ta theo nhiều cách:
Kiểm thử hướng hành vi (BDD) là một phần mở rộng của TDD. Giống như trong TDD thì BDD chúng ta cũng viết các bài kiểm tra trước và thêm mã ứng dụng. Sự khác biệt chính mà chúng ta có thể thấy ở đây là:
Sự khác biệt này dẫn đến nhu cầu phải có một ngôn ngữ có thể định nghĩa với định dạng dễ hiểu.
BDD phổ biến và có thể được sử dụng cho các trường hợp kiểm thử cấp Đơn vị (Unit Test) và cho các trường hợp kiểm thử cấp UI.
Các công cụ như TestNG hoặc JUnit (dành cho Java), RSpec (dành cho Ruby) hoặc trong .NET như MSpec hoặc SpecUnit phổ biến cho Kiểm tra đơn vị theo cách tiếp cận BDD.
Ngoài ra, bạn có thể viết thông số kỹ thuật kiểu BDD về tương tác giao diện người dùng. Giả sử bạn đang xây dựng một ứng dụng web, có thể bạn sẽ sử dụng thư viện tự động hóa trình duyệt như Playwright hoặc Selenium và tạo tập lệnh cho nó bằng cách sử dụng một trong các khung mà An vừa đề cập hoặc một công cụ với format given/when/then chẳng hạn như Cucumber (cho Java) hoặc SpecFlow (cho .NET).
Trong nội dung chính của chúng ta là dùng Cucumber TestNG với Selenium Java nhé @@
Cucumber là một công cụ kiểm thử hay là một Testing Framework hỗ trợ Behavior Driven Development (BDD), cho phép người dùng định nghĩa hành vi của hệ thống với tiếng anh có ý nghĩa đơn giản bằng cách sử dụng một ngữ pháp được xác định bởi một ngôn ngữ gọi là Gherkin.
Cucumber hướng tới việc viết test case mà bất kỳ ai cũng có thể hiểu cho dù họ không có chuyên môn kĩ thuật.
Ví dụ như các nền tảng quen thuộc như Selenium thì thường chỉ người viết test hoặc có kĩ năng lập trình mới hiểu được những gì đang test, còn khách hàng hoặc các bên liên quan thì không đọc ngay code để hiểu mà họ cần hiểu qua tài liệu.
Cucumber ban đầu được thực hiện dành riêng cho ngôn ngữ Ruby và sau đó được mở rộng sang Java, cả Ruby và Java đều sử dụng Junit để chạy test. Sau này là với nhiều ngôn ngữ khác và khung sườn khác như C#, Javascript, Python và cùng với framework như TestNG, specflow,...
Một số lý do sau nên dùng Cucumber:
Feature có thể được hiểu là một đơn vị hoặc chức năng độc lập của một dự án. Ví dụ như một trang web thương mại điện tử, một vài tính năng (features) có thể xác định như:
Trong Cucumber mỗi feature có thể hiểu là mỗi chứ năng độc lập của sản phẩm. Trước khi viết test scripts chúng ta nên xác định trước các features cần test để mang lại hiệu quả cao. Các tests xây dựng trong Cucumber được gọi là các feature files và có dạng .feature, mỗi feature cần test nên đặt trong 1 file feature tương ứng.
Features trong Cucumber được thể hiện bằng ngôn ngữ Gherkin bao gồm các thành phần sau:
Ví dụ test cases dạng Gherkin:
Mặc dù đã có file feature nhưng Cucumber chưa thực sự biết đoạn mã nào sẽ được thực thi cho từng scenario cụ thể được nêu trong file feature. Nó cần một file trung gian Step Definition, file này ánh xạ các bước thực hiện (step), features (Given,When,Then) trong scenario với đoạn mã (code) chức năng cần thực thi. Step được định nghĩa trong file java chẳng hạn như "stepdefinitions/StepLogin.java".
Ví dụ tương ứng với Test Cases Feature trên Login to HRM:
Scenario là cấu trúc lõi của Gherkin. Kịch bản test khai báo với từ khóa "Scenario:" và theo sau là tên kịch bản. Mỗi tính năng có thể có một hoặc nhiều scenarios, mỗi scenario bao gồm một hoặc nhiều steps.
Ví dụ cái Calculator bên trên từ TDD:
Anh Tester
Đường dẫu khó chân vẫn cần bước đi
Đời dẫu khổ tâm vẫn cần nghĩ thấu