CHƢƠNG 3 KIỂM THỬ BAO PHỦ PHẦN MỀM
3.2. Các độ đo bao phủ truyền thống
3.2.1. Bao phủ câu lệnh
Bao phủ câu lệnh (Statement coverage) còn được gọi là bao phủ dòng lệnh (line coverage) là độ đo tỉ lệ phần trăm các câu lệnh chương trình được thực thi khi tiến hành các ca kiểm thử. Tất cả các câu lệnh phải được phủ trong quá trình kiểm thử thực hiện. Đây là cách thức yếu nhất của kiểm thử phủ trong đó một vài nhánh có thế bị thiếu. Lợi ích của bao phủ câu lệnh là khả năng xác định được khối mã đã không được thực thi. Tuy nhiên, nó không cho phép nhận biết lỗi phát sinh từ các dòng điều khiển cấu trúc trong mã nguồn. Chẳng hạn như điều kiện kép hoặc các nhãn được chuyển đổi liên tục. Điều này có nghĩa là, ta có thể dễ dàng có 100% dòng lệnh được bao phủ nhưng vẫn có các lỗi không
được thực thi thì công cụ bao phủ sẽ dùng cờ đánh dấu, những câu lệnh được thực thi sẽ được xuất hiện ra qua bản báo cáo. Đo câu lệnh được bao phủ gần như được xem là cách thực thi đơn giản nhất, nó có thể áp dụng thông qua bytecodes. Bao phủ câu lệnh thường được những người phát triển sử dụng bởi vì nó dễ dàng kết hợp với các dòng mã nguồn. Để có thể tiến hành phân tích kết quả kiểm thử cần tính tỉ lệ phần trăm câu lệnh đã được kiểm tra. Muốn vậy, cần tiến hành hai phép đo chủ yếu đó là: tổng số các câu lệnh trong mã nguồn và số câu lệnh đã được kiểm tra bởi các bộ kiểm thử. Công thức tính phần trăm dòng lệnh trong hình 3.1 đã thực thi được Zan đưa ra [Zand] .
Phần trăm dòng lệnh =
số câu lệnh được kiểm tra*100% Tổng số câu lệnh mã nguồn Hình 3.1 Công thức tính phần trăm bao phủ dòng lệnh
Ví dụ 3.1: Sử dụng mô hình Đồ thị luồng điều khiển để đo mức độ bao phủ câu lệnh của chương trình.
Int proc(int a, int b, int x) { If((a>1)&&(b= =0)) // 1 { x=x/a; //3 } if((a= =2)||(x>1)) //4 { x=x+1; //5 } Return x; //7 }
Cần lựa chọn dữ liệu vào để cho kết quả là các đường đã chọn được thực thi. Các đường đi dưới đây đảm bảo cho việc phủ mọi câu lệnh:
Hình 3.2: Mô hình Đồ thị luồng điều khiển
a > 1 AND b = 0 a == 2 OR x>1 x←x/a x ←x+1 false true true false 1 2 5 4 6 7 3
Dữ liệu vào có thể là: a=2, b=0, x=4
3.2.2. Bao phủ nhánh
Bao phủ nhánh (Branch coverage) còn được gọi là bao phủ quyết định
(decision coverage). Bao phủ nhánh là một phép đo dựa trên các điểm quyết định tại các câu lệnh rẽ nhánh if, while…
Ví dụ 3.2: Về lệnh rẽ nhánh if: if(a>b)
system.out.println(a); else
system.out.println(b);
Báo cáo của cách bao phủ nhánh là báo ra các biểu thức Boolean đã được kiểm tra trong các cấu trúc điều khiển, với cả giá trị “true” và false”. Chẳng hạn, một câu lệnh if(a>b) trong ví dụ trên sẽ chia chương trình thành hai nhánh. Nếu muốn cả hai nhánh đều được thực hiện thì cần phải có những ca kiểm thử để cho a>b trong câu lệnh trên mang cả hai giá trị”true” và “false” tại hai thời điểm nào đó. Nhưng cũng có trường hợp chỉ một ca kiểm thử mà a>b đã có thể nhận được cả hai giá trị này. Chẳng hạn, nếu câu lệnh này nằm trong một vòng lặp thì có thể trong một lần lặp nào đó ta có a>b cho giá trị “true”, nhưng lần lặp sau thì a>b cho giá trị “false”. Công thức tính phần trăm nhánh được bao phủ [Zand] là:
Phần trăm nhánh được bao phủ =
số nhánh được thực thi*100% Tổng số nhánh trong chương trình Hình 3.3: Công thức tính phần trăm nhánh được bao phủ
3.2.3. Bao phủ đường đi
Một đường đi thể hiện một luồng điều khiển thực thi từ khi bắt đầu đến khi kết thúc của một chương trình, một phương thức có N quyết định sẽ có 2N cách đi, và nếu phương thức có vòng lặp thì có thể sẽ có vô số cách đi. Bao phủ đường đi cũng là một trong số các cách đo trong kiểm thử hộp trắng, nó sẽ kiểm
của chương trình từ input đến output. Dựa trên việc xác định các đường đi người ta đưa ra các ca kiểm thử nhằm kiểm tra tất cả các đường đi có thể. Việc xác định các đường đi dựa trên việc phân tích các cấu trúc rẽ nhánh và các vòng lặp.
3.2.4. Bao phủ điều kiện
Bao phủ điều kiện (condition coverage) tương tự như bao phủ nhánh nhưng nó có độ nhạy tốt hơn với luồng điều khiển. Bao phủ điều kiện đo các biểu thức con độc lập với các biểu thức con khác. Bao phủ điều kiện báo cáo kết luận logic “true” hoặc “false” của từng biểu thức Boolean con, các biểu thức Boolean con được phân tách bằng các phép logic-and và logic-or nếu chúng xảy ra.
3.2.5. Bao phủ nhiều điều kiện
Bao phủ nhiều điều kiện (multiple condition coverage) là bao phủ sự xảy ra đồng thời các biểu thức Boolean con trong một biểu thức. Giống với bao phủ điều kiện, các biểu thức con được liên kết bằng các phép logic-and và logic-or.
int proc(int a, int b, int x) { if ( (a>1) && (b==0) ) { x = x/a; } if ( (a==2) || (x>1) ) { x = x+1; } return x; }
Bao phủ đa điều kiện đảm bảo phủ được tất cả nhánh và tất cả câu lệnh tại điểm phân nhánh.
1. a > 1 is true and b = 0 is true 2. a > 1 is true and b = 0 is false 3. a > 1 is false and b = 0 is true 4. a > 1 is false and b = 0 is false 5. a = 2 is true and x > 1 is true 6. a = 2 is true and x > 1 is false 7. a = 2 is false and x > 1 is true 8. a = 2 is false and x > 1 is false
a = 2, b = 0, x = 2 a = 2, b = 1, x = 0 a = 0, b = 0, x = 2 a = 0, b = 1, x = 0
Các đường đi sẽ được thực thi: 1 – 3 – 4 – 5 – 7
1 – 2 – 4 – 5 – 7 1 – 2 – 4 – 5 – 7 1 – 2 – 4 – 6 – 7
3.3. Bao phủ phần mềm hƣớng đối tƣợng
Việc dùng độ đo bao phủ cấu trúc để đo mức hoàn thiện của một tập các ca kiểm thử là một kỹ thuật được xem là tốt. Tuy nhiên, áp dụng kỹ thuật này cho phần mềm hướng đối tượng hiện nay còn nhiều thách thức. Việc đo mức độ bao phủ cấu trúc truyền thống như là: Bao phủ câu lệnh, bao phủ nhánh và bao phủ điều kiện. Làm thế nào để đo được các độ bao phủ này với mỗi phương thức. Đáng tiếc là các độ đo truyền thống không thể đưa vào áp dụng trực tiếp cho kiểm thử phần mềm hướng đối tượng. Đặc biệt, kiểm thử tính đa hình và tính đóng gói đây là những đặc tính tiêu biểu của thiết kế phần mềm hướng đối tượng.
3.3.1. Một cách mới đo mức độ bao phủ
Bao phủ phạm vi (Context coverage) là cách thức tập trung vào dữ liệu của phần mềm được thực thi. Cách tiếp cận bao phủ phạm vi có thể áp dụng cho cả kiểm thử tính đa hình và hành vi phụ thuộc vào trạng thái. Cách tiếp cận này cũng có thể được mở rộng để hỗ trợ trong việc kiểm thử các ứng dụng đa luồng. Bằng cách sử dụng thêm các độ đo bao phủ phạm vi hướng đối tượng kết hợp với các độ đo bao phủ phạm vi truyền thống, chúng ta có thể đảm bảo rằng cấu trúc của mã đã được thực hiện đầy đủ và do đó có thể tin tưởng vào chất lượng
3 a > 1 AND b = 0 a == 2 OR x>1 x←x/a x ←x+1 false true true false 1 2 5 4 6 7
được xác định đó là:
Các độ đo bao phủ phạm vi kế thừa (Inheritance context coverage metrics):
được thiết kế để giúp đo các cuộc gọi đa hình trong hệ thống đã được kiểm thử làm thế nào cho tốt.
Các độ đo bao phủ phạm vi dựa trên trạng thái (State-based context coverage metrics) được thiết kế để cải thiện việc kiểm thử các lớp với hành vi phụ thuộc trạng thái.
Các độ đo bao phủ phạm vi được người dùng xác định (User-defined context coverage metrics) cũng được hỗ trợ, cho phép áp dụng cách tiếp cận bao phủ phạm vi với các trường hợp khác, mà ở đó các độ đo bao phủ cấu trúc truyền thống không đầy đủ, chẳng hạn như các ứng dụng đa luồng.
3.3.2. Bao phủ phạm vi kế thừa
3.3.2.1. Định nghĩa bao phủ phạm vi kế thừa
Bao phủ phạm vi kế thừa không phải là một độ đo duy nhất, mà là một sự mở rộng của các độ đo bao phủ cấu trúc truyền thống khi để ý tới sự thêm vào các tương tác xảy ra với các phương thức được kế thừa.
Bao phủ phạm vi kế thừa cung cấp lựa chọn định nghĩa về độ đo mà cho rằng các mức của bao phủ đạt được trong phạm vi của mỗi lớp như là các độ đo riêng biệt. Các định nghĩa phạm vi kế thừa về bối cảnh coi thực hiện của chương trình trong phạm vi của lớp cơ sở là tách biệt với các thực hiện của chương trình trong phạm vi của lớp kế thừa. Tương tự như vậy, chúng ta xem việc thực hiện của chương trình trong phạm vi của một lớp kế thừa là khác với thực hiện trong phạm vi của một lớp kế thừa khác.
Để đạt được 100% bao phủ phạm vi kế thừa, mã lệnh phải được thực hiện đầy đủ trong từng phạm vi thích hợp.
Bao phủ phạm vi kế thừa là biến thể của bao phủ quyết định cho một chương trình trong một phạm vi đặc biệt đơn giản là số các nhánh quyết định được thực hiện trong phạm vi chia cho tổng số nhánh quyết định trong một chương trình.
nghĩa là tỷ lệ trung bình của bao phủ quyết định phạm vi kế thừa trong mỗi phạm vi thích hợp. Với mỗi chương trình, xác định một lớp cơ sở các phạm vi thích hợp là phạm vi mà tương ứng với mỗi lớp cơ sở song song với các tương ứng mỗi lớp kế thừa mà chương trình không thay đổi. Lưu ý rằng, chương trình không cần phải kiểm thử trong phạm vi của các lớp kế thừa.
3.3.2.2. Bao phủ kế thừa là dễ dàng đạt được
Trong kiểm thử đơn vị, sự cố gắng đạt được bao phủ phạm vi kế thừa là không hơn đáng kể so với những yêu cầu phải đạt được sự bao phủ theo độ đo truyền thống. Điển hình là không cần thêm các ca kiểm thử mới đối với các ca kiểm thử đã được sử dụng cho lớp cơ sở để kiểm thử lại các phương thức kế thừa trong các lớp kế thừa. Ngay với dạng hệ thống có kế thừa song song thì cho phép sử dụng lại các ca kiểm thử cho lớp cơ sở trở để kiểm thử các lớp kế thừa.
Việc sử dụng lại các ca kiểm thử lớp cơ sở còn cho thêm thuận lợi để tiến hành kiểm thử tự động.
3.3.3. Kiểm thử phạm vi dựa trên trạng thái
Phần lớn các hệ thống hướng đối tượng có tồn tại một số các lớp mà có thể được mô tả như là “máy trạng thái”. Các đối tượng của các lớp này có thể tồn tại như một số các trạng thái khác nhau, và hành vi của mỗi lớp có chất lượng khác nhau trong mỗi trạng thái có thể – Hành vi của lớp là các trạng thái phụ thuộc.
Kiểm thử phạm vi dựa vào trạng thái được thiết kế để làm thế nào đo được các hành vi này đã được kiểm thử đầy đủ. Xem xét một lớp cụ thể với trạng thái phụ thuộc hành vi như: Một ngăn xếp bị chặn (hình 3.4) có thể có ba trạng thái: „rỗng‟, „không đầy‟ hoặc „đầy‟. Hành vi của trạng thái có chất lượng khác nhau trong mỗi trạng thái. Ví dụ, pop() thực hiện thao tác di chuyển và trở về phần tử đầu đến ngăn xếp trong trạng thái „không đầy‟ hoặc „đầy‟. Giao diện lớp cho lớp ngăn xếp bị chặn được chỉ ra dưới đây:
Class BoundedStack { Public:
Void push ( int ); Int Pop ( );
Struct underflow: std::exception { }; Struct overflow: std::exception { }; };
Chúng ta có thể sử dụng bao phủ điểm để chắc chắn rằng các ca kiểm thử thực hiện trong mỗi phương thức của lớp. Các ca kiểm thử đạt được 100% bao phủ điểm.
Hình 3.4: Lược đồ chuyển trạng thái cho “ngăn xếp bị chặn”
3.3.3.1. Sử dụng các độ đo bao phủ hộp trắng
Nếu bao phủ điểm vào (entry-point) không đảm bảo kiểm thử kỹ lưỡng, có thể chúng ta nên sử dụng một yêu cầu bao phủ mạnh hơn, là 100% bao phủ quyết định. Thật không may là có các bất lợi khi thông qua các yêu cầu như vậy. Một nhân tố mà có thể được quyết định trong mã mà không tương ứng với các tính năng của giao diện chung. Ví dụ điển hình bao gồm mã điều khiển lỗi
phủ quyết định có thể là khó đạt được.
Một vấn đề quan trọng hơn là sự bất lực của các độ đo bao phủ cấu trúc truyền thống để xác định bao phủ mã khi chúng hoàn toàn vắng mặt. Ví dụ, hãy xem xét những gì sẽ xảy ra nếu ngăn xếp bị chặn quên kiểm tra điều kiện ngăn xếp rỗng trong pop (). Yêu cầu bao phủ quyết định 100% sẽ không giúp tìm lỗi này – các điều kiện thiếu là không có để được bao phủ.
3.3.3.2. Những cách có thể làm tốt hơn
Trên thực tế, chúng ta có thể viết một tập các ca tekiểm thử tốt hơn so với bất kỳ độ đo bao phủ cấu trúc truyền thống nào và không có bất kỳ kiến thức của các chi tiết bên trong lớp. Sơ đồ chuyển trạng thái UML chỉ ra rằng hành vi của lớp thay đổi phụ thuộc vào trạng thái hiện thời. Loại lược đồ này thông thường sử dụng để mô tả trạng thái phụ thuộc hành vi của các lớp
Chúng ta có thể sử dụng thêm thông tin bổ sung của sơ đồ chuyển trạng thái (hình 3.4) để thiết kế một tập các kiểm thử được thực hiện cho các lớp ngăn xếp bị chặn hoàn toàn .
3.3.3.3. Bao phủ nội dung dựa vào trạng thái
Bao phủ phạm vi dựa vào trạng thái là tương tự với bao phủ phạm vi kế thừa: nó cung cấp các định nghĩa khác nhau của các độ đo bao phủ cấu trúc truyền thống. Với bao phủ phạm vi kế thừa các phạm vi phụ thuộc cấu trúc kế thừa của phần mềm được kiểm thử. Tương tự, với bao phủ phạm vi dựa vào trạng thái, phạm vi tương ứng với các trạng thái tiềm năng của đối tượng của lớp kiểm thử. Do đó, các độ đo bao phủ phạm vi dựa vào trạng thái thực hiện một chương trình trong phạm vi của một trạng thái như cách thực hiện của chương trình tương tự trong trạng thái khác
Bao phủ phạm vi dựa vào trạng thái là biến thể của bao phủ điểm cho một lớp (tại phạm vi tương ứng với một trạng thái cụ thể) được cho bởi số lượng các phương thức được gọi vào các đối tượng trong trạng thái chia cho tổng số các phương thức trong lớp.
bình của bao phủ điểm phạm vi dựa vào trạng thái trong mỗi trạng thái phù hợp với lớp.
3.4. Các công cụ phân tích kiểm thử bao phủ
Hiện nay có mộ số công cụ kiểm thử trợ giúp việc kiểm thủ bao phủ. Trong số đó có thể kể đến:
- GCOV: Là một chương trình kiểm thử bao phủ được thiết kế để làm việc riêng với GCC để phân tích chương trình, giúp khởi tạo hiệu quả, giúp mã chạy nhanh hơn. Nó có thể được sử dụng phối hợp với GPROF- một ứng dụng để đánh giá phần của mã, để thời gian tính toán mang số lượng lớn nhất.
- EclEmma: tạo ra các báo cáo và cung cấp phạm vi bao phủ mã để theo dõi thông tin về các ca kiểm thử. Chúng ta sử dụng EclEmma chủ yếu như một công cụ báo cáo mức độ bao phủ. Đối với chương trình nhỏ, bao phủ dễ dàng tính toán bằng tay. Tuy nhiên, đối với các chương trình lớn hơn, công việc gặp nhiều khó khăn hơn cần sự trợ giúp của công cụ. Các công cụ bao phủ, như EclEmma, giúp tự động hoá quá trình đo bao phủ và cung cấp các báo cáo có thể đọc được. EclEmma là công cụ mã nguồn mở, hơn nữa EclEmma có thể được cập nhật tại trang http://update.eclemma.org.
- Cobertura: là một công cụ mã nguồn mở để đo mức độ bao phủ phần