- Cách thực hiện: Lựa chọn các giá trị cận để kiểm thử Nguyên tắc kiểm thử các dữ liệu vào gồm:
7.4.2. Kiểm thử White-bo
1) Ý tƣởng
Phần mềm là một hệ thống gồm 3 thành phần cơ bản: thành phần lƣu trữ, thành phần giao tiếp, thành phần xử lý cần phải thực hiện theo yêu cầu của ngƣời dùng
Thành phần giao tiếp: Giao diện chƣơng trình
Thành phần lƣu trữ: Cho phép lƣu trữ và truy xuất dữ liệu
Thành phần xử lý: Thực hiện các xử lý theo qui trình nghiệp vụ của ngƣời dùng
Hình 7.5. Kiểm thử White-box
Ý tƣởng của phƣơng pháp kiểm thử White-box (hộp trắng): - Quan tâm đến các xử lý tức giải thuật, thuật toán
- Sử dụng và có tính đến các thông tin về cấu trúc chƣơng trình - Thử nghiệm chức năng mà chƣơng trình cung cấp
- Thƣờng tốn rất nhiều thời gian và công sức nếu mức độ kiểm thử đƣợc nâng lên ở cấp kiểm thử tích hợp hay kiểm thử hệ thống.
- Đƣợc dùng để kiểm thử đơn vị. Trong lập trình hƣớng đối tƣợng, kiểm thử đơn vị là kiểm thử từng tác vụ của một class chức năng nào đó.
Do đó, ngƣời kiểm thử (Tester):
- Kiểm thử tính logic và cấu trúc của mã nguồn (source code): bao gồm server code và client.
- Có kiến thức về
+ Ngôn ngữ lập trình (C, C++, VB.NET, Java,…) + Môi trƣờng phát triển phần mềm (IDE)
+ Các hệ quản trị cơ sở dữ liệu (SQL Server, Oracle, DB2,…)
- Kiểm thử tất cả các trƣờng hợp có thể xảy ra trong mã nguồn (cấu trúc điều khiển, cấu trúc lặp,…)
2) Các phƣơng pháp kiểm thử White-box
a. Kiểm thử dựa trên đồ thị luồng điều khiển (Control Flow Graph-CFG)
Kiểm thử dựa trên đồ thị luồng điều khiển là một kỹ thuật kiểm thử hộp trắng do Tom McCabe đề xuất. Phƣơng pháp này cho phép ngƣời thiết kế trƣờng hợp kiểm thử thực hiện phép đo độ phức tạp logic của thiết kế thủ tục và sử dụng phép đo này nhƣ một chỉ dẫn cho việc thiết kế một tập cơ sở các đƣờng dẫn thực hiện.
Những trƣờng hợp kiểm thử đƣợc suy diễn để thực hiện tập cơ sở. Các trƣờng hợp kiểm thử đó đƣợc đảm bảo để thực hiện mỗi lệnh trong chƣơng trình ít nhất một lần trong quá trình kiểm thử.
Mục tiêu của phƣơng pháp kiểm thử luồng điều khiển là đảm bảo mọi đƣờng thi hành của đơn vị phần mềm cần kiểm thử đều chạy đúng. Rất tiếc trong thực tế, công sức và thời gian để đạt mục tiêu trên đây là rất lớn, ngay cả trên những đơn vị phần mềm nhỏ.
Các bƣớc:
Bƣớc 1: Xây dựng đồ thị dòng điều khiển tƣơng ứng từ thủ tục cần kiểm thử Bƣớc2: Tính độ phức tạp Cyclomatic của đồ thị (=C).
Bƣớc 3: Xác định C đƣờng thi hành tuyến tính cơ bản cần kiểm thử. Bƣớc 4: Tạo từng test case cho từng đƣờng thi hành tuyến tính cơ bản. Bƣớc 5: Thực hiện kiểm thử trên tứng test case.
Bƣớc 6: So sánh kết quảcó đƣợc với kết quả đƣợc kỳ vọng.
Bƣớc 7: Lập báo cáo kết quả để phản hồi cho những ngƣời có liên quan.
Xây dựng đồ thị dòng điều khiển tƣơng ứng
- Các cấu trúc điều khiển phổ dụng:
Tính độ phức tạp Cyclomatic của đồ thị (=C)
Độ phức tạp Cyclomatic C = V(G) của đồ thị dòng điều khiển đƣợc tính bởi 1 trong các công thức sau :
- V(G) = E - N + 2, trong đó E là số cung, N là số nút của đồ thị.
- V(G) = P + 1, nếu đồ thị chỉ chứa các nút quyết định luận lý (chỉ có 2 cung xuất True/False) và P số nút quyết định.
Xác định C đƣờng thi hành tuyến tính cơ bản cần kiểm thử.
- Độ phức tạp Cyclomatic C chính là số đƣờng thi hành tuyến tính độc lập cơ bản của thủ tục cần kiểm thử.
- Nếu chúng ta chọn lựa đƣợc đúng C đƣờng thi hành tuyến tính độc lập cơ bản của thủ tục cần kiểm thử và kiểm thử tất cả các đƣờng thi hành này.
- Đƣờng thi hành (Execution path): Là một kịch bản thi hành đơn vị phần mềm tƣơng ứng: danh sách có thứ tự các lệnh đƣợc thi hành ứng với một lần chạy cụ thể của đơn vị phần mềm, bắt đầu từ điểm nhập của đơn vị phần mềm đến điểm kết thúc của đơn vị phần mềm.
Qui trình xác định các đƣờng tuyến tính độc lập
Bƣớc 1: Xác định đƣờng cơ bản, đƣờng này nên là đƣờng thi hành phố biến nhất.
Bƣớc 2: Để chọn đƣờng thứ 2, thay đổi cung xuất của nút quyết định đầu tiên và cố gắng giữ lại maximum phần còn lại.
Bƣớc 3: Để chọn đƣờng thứ 3, dùng đƣờng cơ bản nhƣng thay đổi cung xuất của nút quyết định thứ 2 và cố gắng giữ lại maximum phần còn lại.
Bƣớc 4: Tiếp tục thay đổi cung xuất cho từng nút quyết định trên đƣờng cơ bản để xác định đƣờng thứ 4, 5,... cho đến khi không còn nút quyết định nào trong đƣờng cơ bản nữa.
Bƣớc 5: Lặp dùng tuần tự các đƣờng tìm đƣợc làm đƣờng cơ bản để xác định các đƣờng mới xung quanh nó y nhƣ các bƣớc 2, 3, 4 cho đến khi không tìm đƣợc đƣờng tuyến tính độc lập nào nữa (khi đủ số C).
Ví dụ: Xét thủ tục sau
double average(double value[], double min, double max, int& tcnt, int& vcnt)
{
double sum = 0; int i = 1;tcnt = vcnt = 0; while (value[i] <> -999 && tcnt <100) {
tcnt++;
if (min<=value[i] && value[i] <= max) {sum += value[i]; vcnt ++;} i++; } if (vcnt > 0) return sum/vcnt; return -999;}
Hãy xây dựng các trƣờng hợp kiểm thử dựa trên đồ thị luồng điều khiển
Xây dựng đồ thị dòng điều khiển tƣơng ứng từ thủ tụccần kiểm thử
Tính độ phức tạp Cyclomatic của đồ thị
- Cách 1: C= E - N + 2=18-14+2=6 - Cách 2: C = P+1=5+1 = 6
Xác định C đƣờng thi hành tuyến tính cơ bản cần kiểm thử.
2. 1231011 3. 121012 3. 121012
4. 1234589 5. 12345689 5. 12345689 6. 123456789
Tạo từng test case cho từng đƣờng thi hành tuyến tính cơ bản.
- Test case cho đƣờng 1 : value(k) <>-999, với 1< k < i value(i) = -999 với 2 ≤ i ≤ 100
Kết quả kỳ vọng : (1) average=Giá trị trung bình của k giá trị hợp lệ. (2) tcnt = k. (3) vcnt = k
Chú ý : không thể kiểm thử đƣờng 1 này riêng biệt mà phải khiểm thử chung với đƣờng 4 hay 5 hay 6.
- Test case cho đƣờng 2 :
value(k) <>-999, với k < i , i >100
Kết quả kỳ vọng : (1) average=Giá trị trung bình của 100 giá trị hợp lệ. (2) tcnt = 100. (3) vcnt = 100
- Test case cho đƣờng 3 : value(1) = -999
Kết quả kỳ vọng : (1) average = -999. (2) tcnt = 0 (3) vcnt = 0 - Test case cho đƣờng 4 :
value(i) <> -999 i <= 100 và value(k) < min với k < i
Kết quả kỳ vọng : (1) average=Giá trị trungbình của n giá trị hợp lệ. (2) tcnt = 100. (3) vcnt = n (số lƣợng giá trị hợp lệ)
- Test case cho đƣờng 5 : value(i) <>-999 với i <= 100 và value(k) > max với k <= i
Kết quả kỳ vọng : (1) average=Giá trị trung bình của n giá trị hợp lệ. (2) tcnt = 100. (3) vcnt = n (số lƣợng giá trị hợp lệ)
- Test case cho đƣờng 6 :
value(i) <>-999 và min <= value(i) <= max với i <= 100
Kết quả kỳ vọng : (1) average=Giá trị trung bình của 100 giá trị hợp lệ. (2) tcnt = 100. (3) vcnt = 100
b. Kiểm thử cấu trúc điều khiển
Kiểm thử điều kiện là phƣơng pháp thiết kế trƣờng hợp kiểm thử thực thi các điều kiện logic trong module chƣơng trình.
Một số định nghĩa:
- Điều kiện đơn: là một biến logic hoặc một biểu thức quan hệ, có thể có toán tử NOT (!) đứng trƣớc, ví dụ, NOT (a>b)
- Biểu thức quan hệ: là một biểu thức có dạng E1 <op> E2, trong đó E1, E2 là các biểu thức số học và <op> là toán tử quan hệ có thể là một trong các dạng sau: <, <=, >, >=, = =, !=, ví dụ, a > b+1.
- Điều kiện phức: gồm hai hay nhiều điều kiện đơn, toán tử logic AND (&&) hoặc OR (||) hoặc NOT (!) và các dấu ngoặc đơn “(„ và „)‟, ví dụ, (a > b + 1) AND (a <= max).
Vì vậy, các thành phần trong một điều kiện có thể gồm phép toán logic, biến logic, cặp dấu ngoặc logic (bao một điều kiện đơn hoặc phức), phép toán quan hệ, hoặc biểu thức toán học
Mục đích của kiểm thử điều kiện là để xác định không chỉ các lỗi điều kiện mà cả các lỗi khác trong chƣơng trình. Có một số phƣơng pháp kiểm thử điều kiện đƣợc đề xuất:
- Kiểm thử nhánh (Branch Testing): là phƣơng pháp kiểm thử điều kiện đơn giản nhất.
- Kiểm thử miền (Domain Testing): Cần 3 hoặc 4 kiểm thử cho biểu thức quan hệ. Với một biểu thức quan hệ có dạng E1 <op> E2, cần có 3 kiểm thử đƣợc thiết kế cho E1= = E2, E1 > E2, E1 < E2.
- Kiểm thử nhánh và toán tử quan hệ (Branch and Relational Operator - BRO).
Kiểm thử luồng dữ liệu
Phƣơng pháp kiểm thử luồng dữ liệu lựa chọn các đƣờng dẫn kiểm thử của chƣơng trình dựa vào vị trí khai báo và sử dụng các biến trong chƣơng trình. Với kiểm thử luồng dữ liệu mỗi câu lệnh trong chƣơng trình đƣợc gán số hiệu lệnh duy nhất và mỗi hàm không thay đổi tham số của nó và biến toàn cục. Cho một lệnh với S là số hiệu câu lệnh. Ta định nghĩa,
DEF(S) = là tập các biến đƣợc khai báo trong S. USE(S) = là tập các biến đƣợc sử dụng trong S.
Một chiến lƣợc kiểm thử luồng dữ liệu cơ bản là chiến lƣợc mà mỗi chuỗi DU đƣợc phủ ít nhất một lần. Chiến lƣợc này đƣợc gọi là chiến lƣợc kiểm thử DU. Kiểm thử DU không đảm bảo phủ hết tất cả các nhánh của một chƣơng trình. Tuy nhiên, một nhánh không đảm bảo đƣợc phủ bởi kiểm thử DU chỉ trong rất ít tình huống nhƣ cấu trúc if-then-else mà trong đó phần then không có một khai báo biến nào và có dạng khuyết (không tồn tại phần else). Trong tình huống đó, nhánh else của lệnh if là không
cần thiết phải phủ bằng kiểm thử DU. Chiến lƣợc kiểm thử luồng dữ liệu là rất hữu ích cho việc lựa chọn các đƣờng dẫn kiểm thử của chƣơng trình có chứa các lệnh if hoặc vòng lặp lồng nhau.
Kiểm thử vòng lặp
Kiểm thử vòng lặp là một kỹ thuật kiểm thử hộp trắng mà tập trung trên tính hợp lệ của các cấu trúc lặp.
Việc xây dựng các trƣờng hợp kiểm thử cho mỗi loại cần thực hiện nhƣ sau: - Vòng lặp đơn: Với vòng lặp đơn trong đó N là số lần lặp tối đa, các trƣờng hợp kiểm thử sau đƣợc sử dụng để kiểm thử mỗi điều kiện sau: Bỏ qua vòng lặp, chỉ một lần lặp, hai lần lặp, M lần lặp trong đó M < N; N-1, N, N+1 lần lặp.
- Vòng lặp lồng nhau
+ Bắt đầu tại vòng lặp trong cùng,
+ Xây dựng các kiểm thử vòng lặp đơn cho vòng lặp trong cùng, trong khi đó giữ vòng lặp ngoài cùng tại các giá trị tham số lặp nhỏ nhất của chúng.
+ Phát triển ra phía ngoài, xây dựng các kiểm thử cho vòng lặp tiếp theo, nhƣng giữ tất cả các vòng lặp bên ngoài với giá trị nhỏ nhất và các vòng lặp lồng nhau khác giá trị “đặc biệt”. Tiếp tục cho đến khi tất cả các vòng lặp đƣợc kiểm thử.
- Vòng lặp nối nhau: Nếu các vòng lặp nối nhau là độc lập thì chúng có thể đƣợc xem nhƣ hai vòng lặp đơn riêng biệt, sử dụng phƣơng pháp kiểm thử vòng lặp đơn. Nếu vòng lặp thứ hai phụ thuộc vào vòng lặp trƣớc(ví dụ, biến đếm của vòng lặp 1 là giá trị khởi tạo của vòng lặp 2), thì xem chúng nhƣ các vòng lặp lồng nhau và sử dụng cách tiếp cận kiểm thử vòng lặp lồng nhau.
- Vòng lặp phi cấu trúc: Nếu gặp các lớp vòng lặp này chúng ta sẽ không kiểm thử mà sẽ thiết kế lại tƣơng ứng với sử dụng việc xây dựng chƣơng trình có cấu trúc.
c. Kiểm thử đột biến (mutation testing)
Kiểm thử đột biến đƣợc đề xuất đầu tiên năm 1978 bởi DeMillo, và đƣợc thiết kế để tạo ra một bộ dữ liệu kiểm thử hiệu quả có khả năng phát hiện lỗi của chƣơng trình. Kiểm thử đột biến tập trung vào việc đánh giá khả năng phát hiện lỗi của dữ liệu dùng để kiểm thử. Kiểm thử đột biến đƣợc dùng kết hợp với các kỹ thuật kiểm thử thông thƣờng nhƣng không thể đƣợc dùng để thay thế cho các kỹ thuật kiểm thử thông thƣờng đó.
Kiểm thử đột biến là một kỹ thuật kiểm thử hộp trắng hay kiểm thử cấu trúc, đƣợc xây dựng dựa vào hai giả thuyết cơ bản:giả thuyết “lập trình viên giỏi” (competent programmer) và giả thuyết “hiệu ứng liên kết” (coupling effect). Giả thuyết “lập trình viên giỏi” giả thiết rằng lập trình viên chỉ phạm những lỗi đơn giản do sơ suất. Giả thuyết “hiệu ứng liên kết” giả thuyết rằng, nếu dữ liệu thử phát hiện đƣợc các lỗi đơn giản thì dữ liệu đó cũng cho phép phát hiện các lỗi phức tạp.
Kiểm thử đột biến bao gồm việc tạo ra các phiên bản lỗi của chƣơng trình gốc đƣợc kiểm thử nhờ vào các toán tử đột biến. Các phiên bản lỗi đó đƣợc gọi là các đột biến (mutant).
Hình 7.6. Kiểm thử đột biến