Nội dung xuyên suốt của luận văn là nghiên cứu kỹ thuật kiểm thử hộp trắng đi sâu vào kiểm thử luồng điều khiển để phân tích các đường đi trong chương trình.. người lập trình lại đóng va
Trang 1ĐẠI HỌC QUỐC GIA HÀ NỘI TRƯỜNG ĐẠI HỌC CÔNG NGHỆ
LÊ THỊ THU HIỀN
KIỂM TRA ĐỘ PHỦ TRONG KIỂM THỬ ĐƠN VỊ
LUẬN VĂN THẠC SĨ CÔNG NGHỆ THÔNG TIN
Hà Nội, 2014
Trang 2ĐẠI HỌC QUỐC GIA HÀ NỘI TRƯỜNG ĐẠI HỌC CÔNG NGHỆ
LÊ THỊ THU HIỀN
KIỂM TRA ĐỘ PHỦ TRONG KIỂM THỬ ĐƠN VỊ
Ngành: Công nghệ thông tin
Chuyên ngành: Kỹ thuật phần mềm
Mã số: 60480103
LUẬN VĂN THẠC SĨ CÔNG NGHỆ THÔNG TIN
NGƯỜI HƯỚNG DẪN KHOA HỌC: PGS.TS NGUYỄN VIỆT HÀ
Hà Nội, 2014
Trang 3i
LỜI CẢM ƠN!
Trước hết tôi xin gửi lời cảm ơn đặc biệt nhất tới PGS.TS Nguyễn Việt
Hà, Bộ môn Công nghệ phần mềm, Khoa Công nghệ thông tin, Trường Đại học Công nghệ, Đại học Quốc Gia Hà Nội, người đã định hướng đề tài và tận tình hướng dẫn chỉ bảo tôi trong suốt quá trình thực hiện luận văn cao học này
Tôi xin được gửi lời cảm ơn sâu sắc tới các thầy cô giáo khoa Công nghệ thông tin, Đại học Công nghệ, Đại học Quốc Gia Hà Nội đã tận tình giảng dạy
và truyền đạt những kiến thức, những kinh nghiệm quý báu trong suốt hai năm học Cao học
Cuối cùng tôi xin dành tình cảm biết ơn tới Bố, Mẹ, Chồng và gia đình, những người đã luôn luôn ở bên cạnh tôi, động viên, chia sẻ cùng tôi trong suốt thời gian học cao học cũng như quá trình thực hiện luận văn cao học
Hà Nội, tháng 06 năm 2014
Lê Thị Thu Hiền
Trang 4ii
LỜI CAM ĐOAN
Tôi xin cam đoan đây là công trình nghiên cứu của riêng tôi Các kết quả nêu trong bản luận văn này là trung thực và chưa từng được ai công bố trong bất
cứ công trình nào khác
Hà Nội, tháng 06 năm 2014
Lê Thị Thu Hiền
Trang 5iii
TÓM TẮT
Kiểm thử phần mềm có một vai trò quan trọng trong việc đảm bảo tính đúng đắn của hệ thống phần mềm trong suốt quá trình thực thi Kiểm thử cần được tiến hành ở nhiều mức và phối hợp nhiều kỹ thuật khác nhau Kiểm thử đơn vị mặc dù không còn quá mới mẻ tuy nhiên nó vẫn là một trong những bước kiểm thử quan trọng khi viết chương trình Nội dung xuyên suốt của luận văn là nghiên cứu kỹ thuật kiểm thử hộp trắng đi sâu vào kiểm thử luồng điều khiển để phân tích các đường đi trong chương trình Độ phủ là một trong các tiêu chí khi kiểm thử luồng điều khiển, độ phủ càng lớn thì độ tin cậy của bộ dữ liệu kiểm thử càng cao Khi chương trình tồn tại các nhánh chưa được phủ thì lỗi rất có thể xảy ra tại các nhánh này Luận văn đề xuất phương pháp kiểm tra độ phủ của bộ
dữ liệu kiểm thử đạt tiêu chuẩn bao phủ nhánh dựa trên công cụ kiểm thử Java PathFinder (JPF) Phương pháp đã sử dụng các chức năng lưu vết khi thực thi chương trình của JPF để xây dựng một công cụ tự động hỗ trợ kiểm tra độ phủ trong kiểm thử đơn vị Công cụ xây dựng đã hỗ trợ lập trình viên kiểm tra độ phủ của bộ dữ liệu kiểm thử cũng như giúp lập trình viên đánh giá lại mã nguồn
đã viết
Trang 6iv
MỤC LỤC
DANH MỤC CÁC THUẬT NGỮ, KÝ HIỆU VÀ CHỮ VIẾT TẮT vi
DANH MỤC CÁC BẢNG vii
DANH MỤC CÁC HÌNH VẼ, ĐỒ THỊ viii
CHƯƠNG 1- MỞ ĐẦU 1
1.1 Bối cảnh nghiên cứu 1
1.2 Nội dung nghiên cứu 2
1.3 Cấu trúc luận văn 3
CHƯƠNG 2- CƠ SỞ LÝ THUYẾT VỀ KIỂM THỬ 4
2.1 Khái niệm kiểm thử phần mềm 4
2.2 Quy trình kiểm thử phần mềm 4
2.3 Kiểm thử đơn vị 5
2.3.1 Kiểm thử đơn vị trong vòng đời phát triển phần mềm 5
2.3.2 Một số loại kiểm thử đơn vị 6
2.4 Các kỹ thuật kiểm thử phần mềm 7
2.4.1 Kỹ thuật kiểm thử hộp đen 7
2.4.2 Kỹ thuật kiểm thử hộp trắng 9
CHƯƠNG 3 – CÔNG CỤ KIỂM CHỨNG JAVA PATHFINDER 16
3.1 Lịch sử của Java PathFinder 16
3.2 Những gì có thể được kiểm chứng bởi Java PathFinder 16
3.3 Kiến trúc của Java PathFinder 17
3.4 Một số phần mở rộng của Java PathFinder 19
3.4.1 Bộ tạo chỉ thị (Bytecode Factory) 19
3.4.2 Listeners 20
3.4.3 Hệ thống báo cáo (The Report) 23
3.5 Đo độ phủ trong kiểm thử đơn vị sử dụng JPF CoverageAnalyzer 24
3.6 Kết luận 28
CHƯƠNG 4 – PHƯƠNG PHÁP KIỂM TRA ĐỘ PHỦ TRONG KIỂM THỬ ĐƠN VỊ SỬ DỤNG JAVA PATHFINDER 29
Trang 7v
4.1 Bài toán 29
4.2 Phương pháp xây dựng công cụ kiểm tra độ phủ sử dụng JPF 29
4.2.1 Nội dung phương pháp 29
4.2.2 Ví dụ minh họa cho phương pháp 33
4.3 Kết luận 35
CHƯƠNG 5 – THỰC NGHIỆM 36
5.1 Giới thiệu về công cụ kiểm tra độ phủ 36
5.2 Cài đặt công cụ sử dụng lập trình 36
5.3 Xây dựng công cụ kiểm tra độ phủ 37
5.3.1 Phân tích dữ liệu log 38
5.3.2 Phân tích mã nguồn 41
5.3.3 So sánh dữ liệu log và mã nguồn báo cáo kết quả 43
5.3.4 Các bước thực thi chương trình 43
5.4 Thực nghiệm kiểm tra độ phủ 44
KẾT LUẬN 52
TÀI LIỆU THAM KHẢO 53
PHỤ LỤC 55
Trang 8vi
DANH MỤC CÁC THUẬT NGỮ, KÝ HIỆU VÀ CHỮ VIẾT TẮT
EO Expected output(Kết quả mong đợi)
Statement coverage Bao phủ câu lệnh
Branch coverage Bao phủ nhánh, bao phủ quyết định
Condition coverage Bao phủ điều kiện
Test case Ca kiểm thử
Test suite Bộ các ca kiểm thử, bộ dữ liệu kiểm thử
Valication Thẩm định
Verification Kiểm chứng
Trang 9vii
DANH MỤC CÁC BẢNG
Bảng 2.1: Các tiêu chuẩn thiết kế ca kiểm thử 10
Bảng 3.1: Bộ test theo tiêu chuẩn bao phủ điều kiện của hàm exam 25
Bảng 3.2: Bộ test theo tiêu chuẩn bao phủ câu lệnh của hàm exam 26
Bảng 3.3: Bộ test theo tiêu chuẩn bao phủ nhánh của hàm exam 27
Bảng 5.1: Bộ test theo tiêu chuẩn bao phủ câu lệnh của hàm foo 44
Bảng 5.2: Bộ test theo tiêu chuẩn phủ nhánh của hàm foo 45
Bảng 5.3: Bộ test theo tiêu chuẩn bao phủ nhánh của hàm getAverage 47
Bảng 5.4: Bộ test kiểm thử vòng lặp while trong hàm getAverage 49
Bảng 5.5: Kết quả thực nghiệm đánh giá độ phủ của bộ dữ liệu 50
Bảng 5.6: Kết quả thực nghiệm kiểm thử vòng lặp 50
Trang 10viii
DANH MỤC CÁC HÌNH VẼ, ĐỒ THỊ
Hình 2.1: Mô hình kiểm thử chữ V 5
Hình 2.2: Sơ đồ vòng lặp while , do while 13
Hình 2.3: Sơ đồ mô tả vòng lặp lồng nhau 14
Hình 3.1: Kiến trúc JPF 17
Hình 3.2: Bộ tạo chỉ thị Bytecode Factory 19
Hình 3.3: JPF Listeners 20
Hình 3.4: Các loại Listener 21
Hình 3.5: Hệ thống báo cáo Report 24
Hình 3.6: Quy trình kiểm tra độ phủ hàm sử dụng JPF CoverageAnalyzer 25
Hình 3.7: Đồ thị luồng điều khiển của hàm exam 25
Hình 3.8: Minh họa kết quả đo độ phủ sử dụng JPF CoverageAnalyzer 26
Hình 3.9: Kết quả kiểm tra độ phủ câu lệnh của hàm exam 27
Hình 3.10: Kết quả kiểm tra độ phủ nhánh của hàm exam 28
Hình 4.1: Quy trình tổng quan xây dựng công cụ kiểm tra độ phủ 30
Hình 4.2: Vết ngăn xếp chương trình 32
Hình 4.3: Minh họa kết quả đạt được của phương pháp đề xuất 34
Hình 5.1: Kết quả biên dịch thành công chương trình JPF trên eclipse 37
Hình 5.2: Màn hình console hiển thị chạy thành công JPF 37
Hình 5.3: Biểu đồ tuần tự giữa các lớp trong chương trình 38
Hình 5.4: Biểu đồ tuần tự quá trình phân tích dữ liệu vết(log) 39
Hình 5.5: Biểu đồ lớp phân tích dữ liệu vết 40
Hình 5.6: Biểu đồ tuần tự quá trình phân tích mã nguồn 42
Hình 5.7: Biểu đồ lớp các lớp lưu trữ thông tin đọc từ mã nguồn 42
Hình 5.8: Màn hình console thực thi chương trình 43
Hình 5.9: Minh họa kết quả kiểm tra với công cụ 43
Hình 5.10: Đồ thị luồng điều khiển cho hàm foo 44
Hình 5.11: Kết quả kiểm tra độ phủ của hàm foo thực thi bộ test ở bảng 5.2 45
Hình 5.12: Kết quả thông báo lỗi khi thực thi hàm foo với bộ test tại bảng 5.2 46 Hình 5.13: Kết quả kiểm tra độ phủ của hàm getAverage thực thi với bộ test case tại bảng 5.3 48
Hình 5.14: Kết quả kiểm thử vòng lặp while trong hàm getAverage 49
Trang 11CHƯƠNG 1- MỞ ĐẦU
1.1 Bối cảnh nghiên cứu
Trong những năm gần đây, khi công nghệ thông tin càng ngày càng phát triển, phần mềm thực sự trở thành một phần không thể thiếu trong các doanh nghiệp Mỗi bộ phận trong mỗi doanh nghiệp đều phụ thuộc vào phần mềm để
hỗ trợ việc phát triển, sản xuất, quảng cáo và tiếp thị các sản phẩm và dịch vụ của họ Trong các giai đoạn phát triển phần mềm, giai đoạn phát hiện, xác định
và sửa các lỗi phần mềm là giai đoạn không thể thiếu nhằm đảm bảo chất lượng của các sản phẩm phần mềm Trong một tổ chức phát triển thương mại điển hình, chi phí dành cho các công việc gỡ lỗi (debugging), kiểm thử (testing) và các hoạt động kiểm chứng phần mềm (verification activities) chiếm từ 50 đến 70% tổng chi phí phát triển [7]
Với tốc độ phát triển đến chóng mặt của lĩnh vực công nghệ thông tin và truyền thông trên cả các hệ thống phần cứng và phần mềm, khả năng xảy ra nhiều lỗi, đặc biệt là các lỗi phức tạp là rất cao Những lỗi này có thể gây ra những hậu quả nghiêm trọng về tiền bạc, thời gian, thậm chí sinh mạng của con người Nhìn chung, một lỗi càng sớm được phát hiện sẽ càng mất ít công sức để sửa lỗi, thậm chí có thể phải xây dựng lại toàn bộ hệ thống từ đầu
• Theo thống kê của Standish Group, Mỹ (2000) [8]: Trên 350 công ty
với hơn 8000 dự án phần mềm có: 31% dự án phần mềm bị huỷ bỏ trước khi được hoàn thành Với các công ty lớn, chỉ có khoảng 9% tổng số các dự án hoàn thành đúng tiến độ và trong ngân sách dự án (với các công ty nhỏ, tỷ lệ này vào khoảng 16%)
• Theo nghiên cứu của NIST, Mỹ (2002) [10]: Chi phí hàng năm dành
cho việc phát hiện các lỗi phần mềm lên đến 59.5 tỉ đô la chiếm từ 0.2 đến 0.6% GDP kinh tế nước Mỹ
• Theo thống kê của NASA IV&V Center (2000) [10]: Ngành công
nghiệp không gian vũ trụ mất đến hàng tỉ đô la và hàng trăm sinh mạng con người trong những năm cuối thập niên 1990 vì các vấn đề liên quan đến phần mềm
Quá trình phát triển phần mềm bao gồm rất nhiều giai đoạn: Thu thập yêu cầu, phân tích, thiết kế, xây dựng, kiểm tra, triển khai và bảo trì phần mềm Trong các giai đoạn đó giai đoạn kiểm tra, phát hiện, xác định và sửa các lỗi phần mềm là rất quan trọng để đảm bảo chất lượng của một phần mềm Để phát hiện ra các lỗi phần mềm, phần mềm cần được kiểm chứng (Verification) và
Trang 12thẩm định (Validation) [9, 11] Thẩm định cần có sự tham gia của khách hàng nhằm kiểm tra xem phần mềm có thực sự đáp ứng các yêu cầu của khách hàng hay không Kiểm chứng là kiểm tra phần mềm có được thiết kế và thực thi đúng như đặc tả hay không Mục tiêu chính của phát triển phần mềm là phải làm sao tạo ra được những sản phầm phần mềm có chất lượng tốt nhất Để giúp làm được điều đó kiểm chứng phần mềm là phần không thể thiếu Kiểm chứng phần mềm giúp làm giảm thiểu lỗi phần mềm tới mức có thể chấp nhận được Chính
vì vậy, nó có vai trò vô cùng quan trọng trong toàn bộ quy trình phát triển phần mềm và trong ngành công nghệ phần mềm hiện nay Chính vì thế, trong công nghệ phần mềm, kiểm chứng phần mềm luôn thu hút được mối quan tâm của rất nhiều nhà nghiên cứu Việc viết tập hợp các ca kiểm thử (test cases) là một phần quan trọng không thể thiếu trong phương pháp kiểm thử phần mềm Tập hợp các
ca kiểm thử đúng đắn giúp giảm thiểu tối đa các lỗi, giảm thời gian tìm kiếm lỗi, tạo ra được các phần mềm tốt, tính ổn định cao
1.2 Nội dung nghiên cứu
Kiểm thử là giai đoạn quan trọng trong quá trình phát triển phần mềm Có rất nhiều nguyên nhân gây ra lỗi trong phần mềm, nó nằm ở tất cả các giai đoạn trong quy trình phát triển phần mềm, và lập trình cũng là một trong số các giai đoạn đó Lỗi xuất hiện do lập trình gây ra là hoàn toàn dễ hiểu, trong thời kỳ đầu của ngành công nghiệp phần mềm, phát triển phần mềm cũng có nghĩa là lập trình, việc lập trình được thực hiện hoàn toàn thủ công, chính vì thế lỗi gây ra do lập trình là chủ yếu Ngày nay, lập trình chỉ là một công đoạn trong quá trình phát triển phần mềm, được hỗ trợ bởi nhiều công cụ lập trình cao cấp, do đó việc lập trình đã trở nên nhẹ nhàng hơn, tuy nhiên không phải vì vậy mà lỗi lập trình lại mất đi Người ta áp dụng rất nhiều kỹ thuật trong kiểm thử nhằm mục tiêu thiết kế ra các ca kiểm thử có khả năng phát hiện ra nhiều lỗi nhất trong chương trình Tuy nhiên, trên thực tế vẫn có thể tồn tại các nhánh không được chạy qua khi hàm hoặc một phương thức được thực thi Những nhánh đó có thể là vô nghĩa nhưng cũng có thể là nơi tiềm ẩn rất nhiều nguyên nhân gây ra lỗi Vì vậy, chúng ta cần có phương pháp xác định có hay không tồn tại các nhánh chưa duyệt qua trong chương trình, nếu có thì thông báo cho người phát triển kiểm tra
và xử lý Trong nội dung luận văn sẽ tìm hiểu các tiêu chuẩn bao phủ [6] mã nguồn của bộ test cơ bản nhưng rất mạnh mẽ như tiêu chuẩn bao phủ lệnh (statement coverage), bao phủ nhánh (branch coverage), bao phủ điều kiện (condition coverage) nhằm giúp xây dựng các ca kiểm thử tốt phát hiện nhánh chưa được phủ trong mã nguồn đang xét Bên cạnh đó trong luận văn cũng đề
Trang 13xuất xây dựng một công cụ tự động kiểm tra độ phủ của bộ dữ liệu kiểm thử thỏa mãn một tiêu chuẩn dựa trên công cụ kiểm chứng Java Pathfinder Đầu vào của công cụ là một hàm hay một phương thức cần kiểm tra, xây dựng đồ thị luồng điều khiển, tiến hành xây dựng các ca kiểm thử Sử dụng công cụ Java Pathfinder hỗ trợ ghi lại từng dòng lệnh được chạy trong quá trình kiểm thử với
bộ các ca kiểm thử đã xây dựng Sau đó công cụ so sánh dữ liệu ghi được với các nhánh trong đồ thị luồng điều khiển, xác định được những nhánh nào trong hàm, trong phương thức chưa được phủ, hiển thị kết quả cảnh báo cho lập trình viên Công cụ sẽ giúp quá trình kiểm tra độ phủ trong kiểm thử đơn vị thực hiện nhanh chóng, kết quả hiển thị trực quan hơn nhằm đánh giá các ca kiểm thử và hàm đang xét
1.3 Cấu trúc luận văn
Phần còn lại của luận văn có cấu trúc như sau:
Chương 2: Trình bày kiến thức về kiểm thử phần mềm, chiến lược kiểm thử
đơn vị với kỹ thuật cụ thể là kiểm thử luồng điều khiển trong kiểm thử hộp trắng
Chương 3: Giới thiệu về công cụ kiểm chứng JavaPathfinder, các kiến trúc
chung về JPF, thành phần mở rộng của JPF và cách kiểm tra độ phủ của bộ dữ liệu kiểm thử thông qua tính năng trong lớp CoverageAnalyzer
Chương 4: Đề xuất phương pháp kiểm tra độ phủ của bộ dữ liệu kiểm thử dựa
trên công cụ Java Pathfinder Nội dung của phương pháp là xây dựng công cụ đo
độ phủ ở mức kiểm thử đơn vị sử dụng khả năng lưu vết chương trình của JPF
Chương 5: Thực nghiệm và đánh giá kết quả đạt được Trong chương này
chúng tôi nêu rõ cách cài đặt công cụ JPF, vận dụng tính năng của JPF và cài đặt thêm mã nguồn để xây dưng công cụ kiểm tra độ phủ Bên cạnh đó chúng tôi sử dụng công cụ đã xây dụng để kiểm tra độ phủ của bộ test thông qua các ví dụ từ
đó rút ra nhận xét đánh giá về mã nguồn cũng như các ca kiểm thử đã viết
Trang 14CHƯƠNG 2- CƠ SỞ LÝ THUYẾT VỀ KIỂM THỬ 2.1 Khái niệm kiểm thử phần mềm
Kiểm thử phần mềm là quá trình thực thi hệ thống hoặc chương trình để kiểm tra xem hệ thống có đúng với đặc tả, thiết kế hay không Kiểm thử phần mềm đồng nghĩa với việc tìm ra những lỗi tiềm ẩn chưa được phát hiện, một cách sớm nhất và đảm bảo rằng những lỗi đó đã được sửa, chứ không làm công việc chuẩn đoán nguyên nhân gây ra lỗi hay sửa lỗi đã phát hiện được[4]
Mục đích của kiểm thử phần mềm là thiết kế một hoặc chuỗi các ca kiểm thử
có khả năng phát hiện được lỗi cao Để thu được kết quả tốt, hoạt động kiểm thử cần được chuẩn bị tốt về kế hoạch, thiết kế và dữ liệu cho mỗi ca kiểm thử Đây chính là đầu vào cho việc kiểm thử, còn đầu ra chính là tài liệu báo cáo kiểm thử, nêu ra được các thông tin về mục đích của kiểm thử, dữ liệu đầu vào, dữ liệu đầu ra mong đợi, dữ liệu đầu vào thực tế…
2.2 Quy trình kiểm thử phần mềm
Quy trình kiểm thử phần mềm bao gồm các bước như lập kế hoạch kiểm thử, thiết kê các trường hợp kiểm thử, bố trí nhân lực kiểm thử, đo lường, đánh giá sản phẩm để xác nhận sản phẩm có thể được đưa ra sử dụng hay chưa Trong đó:
Giai đoạn lập kế hoạch kiểm thử [2,3] bao gồm các bước như xác định hoạt động cần thực hiện, phương pháp thực hiện, mục đích kiểm thử dựa vào đặc tả yêu cầu, đặc tả chức năng…, phạm vi kiểm thử, phương pháp kiểm thử, nguồn tài nguyên cần thiết, thời gian biểu cho hoạt động kiểm thử, tài liệu tham khảo
Giai đoạn thiết kế các ca kiểm thử là giai đoạn quan trọng nhất, bao gồm các bước xác định dữ liệu đầu vào và đầu ra cho hoạt động kiểm thử cùng các câu lệnh cần thực hiện Trong suốt quá trình thiết kế ca kiểm thử, yêu cầu hệ thống phải được nghiên cứu một cách thận trọng, các tính năng của hệ thống phải được xác định một cách rõ ràng và các hành vi của ca kiểm thử cũng cần được định nghĩa chi tiết
Bố trí nhân lực kiểm thử: việc kiểm thử cần được tiến hành một cách độc lập giữa các nhóm kiểm thử sau đó thu thập kết quả của từng nhóm, so sánh với nhau và đưa ra kết luận cuối cùng Việc kiểm thử thường được tiến hành ở nhiều cấp độ khác nhau như kiểm thử đơn vị, kiểm thử tích hợp hay kiểm thử hệ thống…, xuyên suốt trong vòng đời phát triển của phần mềm Ở mỗi cấp độ kiểm thử nên có từng nhóm riêng biệt ví dụ ở cấp độ kiểm thử đơn vị thì chính
Trang 15người lập trình lại đóng vai trò của người kiểm thử, ở cấp độ kiểm thử tích hợp được thực hiện với các kỹ sư kiểm thử tích hợp hệ thống, họ là những người có kiến thức về cơ chế xây dựng, tích hợp những hệ thống lớn…
2.3 Kiểm thử đơn vị
2.3.1 Kiểm thử đơn vị trong vòng đời phát triển phần mềm
Quy trình kiểm thử được thực hiện ở nhiều cấp độ khác nhau trong vòng đời phát triển phần mềm, bao gồm kiểm thử hoàn toàn hoặc kiểm thử một phần hệ thống Mỗi hệ thống phần mềm thường trải qua bốn cấp độ kiểm thử là kiểm thử đơn vị ( unit testing), kiểm thử tích hợp ( integration testing), kiểm thử hệ thống ( system testing) và kiểm thử chấp nhận ( acceptance testing) trước khi được đưa vào triển khai thực tế Trong đó chỉ có kiểm thử chấp nhận được thực hiện bởi khách hàng, còn ba loại kiểm thử đầu tiên đều được thực hiện bởi các nhóm phát triển trong đơn vị sản xuất phần mềm Bốn loại kiểm thử trên kết hợp với nhau tạo thành mô hình chữ V
Hình 2.1: Mô hình kiểm thử chữ V
Trong kiểm thử đơn vị, người lập trình đóng luôn vai trò của người kiểm thử, họ sẽ kiểm tra từng bộ chương trình riêng lẻ như hàm, thủ tục, lớp, chức năng một cách độc lập Sau khi đảm bảo rằng các bộ chương trình đó làm việc tốt trong phạm vị cho phép thì các mô đun sẽ được kết hợp lại thành các hệ thống con lớn hơn Có hai lý do chính cho việc tiến hành kiểm thử đơn vị một cách độc lập:
Đầu tiên, khi tiến hành kiểm thử đơn vị một cách riêng biệt, nếu lỗi được tìm thấy thì nó có thể sửa một cách dễ dàng, do chúng có thể được xác định rõ ràng trong bộ chương trình nào và dữ liệu đầu vào cho từng bộ chương trình cũng là
Yêu cầu nghiệp vụ
Trang 16đơn giản Nếu nhiều bộ chương trình được kết hợp lại với nhau để kiểm thử, thì người kiểm thử sẽ phải tạo ra dữ liệu kiểm thử không chỉ cho từng bộ chương trình, mà còn phải cho cả mối quan hệ giữa các bộ chương khác với nhau
Thứ hai, trong suốt quá trình thực thi kiểm thử đơn vị, việc thực thi từng bộ chương trình một cách riêng lẻ có thể đảm bảo việc thực thi từng đường dẫn riêng lẻ nhiều nhất có thể Điều đó dẫn tới việc nhiều đường đi độc lập nhất có thể được kiểm tra Khi thực hiện kiểm thử đơn vị, người kiểm thử phải đảm bảo được một số nguyên tắc như thực thi mọi dòng lệnh, biểu thức điều kiện, vòng lặp…và đảm bảo rằng nó không chứa các lỗi có thể tìm thấy được trước khi được kết hợp với các bộ khác
2.3.2 Một số loại kiểm thử đơn vị
Kiểm thử đơn vị gồm hai loại cơ bản là kiểm thử đơn vị tĩnh( static unit testing) và kiểm thử đơn vị động (dynamic unit testing)
Kiểm thử đơn vị tĩnh
Với kiểm thử đơn vị tĩnh, mã nguồn được đánh giá nhờ hai kỹ thuật chính là walkthrough và inspection Trong đó, theo Edward Yourdon [5] thì kỹ thuật walkthrough được thực hiện theo cách tác giả của chương trình sẽ hướng dẫn nhóm kiểm thử thông qua việc thực thi chương trình một cách cụ thể với những kịch bản đã được giàn dựng từ trước Theo Michael Fagan [5] thì kỹ thuật inspection có nghĩa là trong mỗi bước kiểm tra, từng nhóm sẽ đánh giá sản phẩm bằng cách so sánh với kịch bản kiểm thử đã được chuẩn bị từ trước Cả hai kỹ thuật trên đều là đánh giá mã nguồn theo hướng ngữ nghĩa và không đánh giá người tạo ra mã nguồn
Quy trình kiểm thử đơn vị tĩnh được thực hiện lần lượt qua các bước: chuẩn
bị kịch bản, nhân lực -> chuẩn bị câu hỏi, yêu cầu thay đổi -> kiểm tra, đánh giá
mã nguồn -> tác giả mã nguồn sửa những vấn đề thay đổi -> xác minh lại -> kết thúc
Kiểm thử đơn vị động
Kiểm thử đợn vị động là kỹ thuật kiểm thử dựa vào việc thực thi từng bộ chương trình một cách độc lập trong môi trường thực thi được mô phỏng Nó gồm có hai thành phần chính là test driver và stubs
Test driver là một chương trình gọi tới các bộ để kiểm thử Các bộ đó sẽ thực thi với dữ liệu đầu vào được sinh ra từ driver và trả lại giá trị cho driver Driver sẽ so sánh đầu ra thực tế với đầu ra mong đợi rồi báo kết cáo lại kết quả kiểm thử Driver không chỉ giữ vai trò biên dịch chương trình mà nó còn cung cấp cả dữ liệu đầu vào cho kiểm thử theo định dạng mong muốn
Trang 17 Stubs là chương trình con mô phỏng, thay thế cho mỗi bộ chương trình nào đó được gọi tới từ bộ chương trình đang được kiểm tra Mỗi stub đảm nhiệm hai nhiệm vụ, đầu tiên nó chỉ ra vai trò của chính nó làm nhiệm vụ bằng cách in
ra các thông điệp Thứ hai, nó trả lại giá trị mong muốn cho bộ chương trình gọi tới nó, giúp cho bộ đó có thể tiếp tục thực thi
Trong kiểm thử đơn vị động, có một số kỹ thuật giúp cho việc lựa chọn dữ liệu đầu vào kiểm thử đạt hiệu quả cao như kiểm thử luồng điều khiển (control flow testing-), kiểm thử luồng dữ liệu (data flow testing), kiểm thử miền giá trị (domain testing)…
Kiểm thử luồng điều khiển được thực hiện thông qua một số bước như vẽ
đồ thị luồng điều khiển từ bộ chương trình, lựa chọn một vài kịch bản kiểm thử luồng điều khiển, xác định đường đi trong đồ thị luồng điều khiển thỏa mãn điều kiện của kịch bản kiểm thử, xác định biểu thức từ các đường đi được lựa chọn rồi sinh ra dữ liệu phục vụ cho việc kiểm thử
Kiểm thử luồng dữ liệu: trong kiểm thử luồng dữ liệu đầu tiên cũng phải
vẽ được đồ thị luồng dữ liệu, sau đó tới việc lựa chọn các kịch bản kiểm thử luồng dữ liệu, xác định đường đi trong đồ thị luồng dữ liệu thỏa mãn kịch bản kiểm thử, xác định biểu thức từ các đường đi được lựa chọn rồi sinh ra dữ liệu kiểm thử
Kiểm thử miền giá trị: trong cả hai kỹ thuật kiểm thử luồng điều khiển và kiểm thử luồng dữ liệu đề không có cơ chế phát hiện được lỗi, điều này có thể được giải quyết trong kỹ thuật kiểm thử miền giá trị(domail testing) Trong các tiếp cận này, danh mục các lỗi miền giá trị sẽ được liệt kê, sau đó dữ liệu kiểm thử sẽ được lựa chọn trong danh mục đó để phát hiện lỗi Phương pháp này sẽ phải phụ thuộc rất nhiều vào kinh nghiệm của người kiểm thử
2.4 Các kỹ thuật kiểm thử phần mềm
Theo John D Mc Gregor[13] thì các kỹ thuật kiểm thử cũng giống như những công cụ được thiết kế nhằm đảm bảo rằng tất cả các khía cạnh của hệ thống đều được khảo sát qua Những kỹ thuật đó chính là công cụ mang lại hiệu quả, đảm bảo chất lượng cho hệ thống Có hai loại kỹ thuật kiểm thử phần mềm chính là kỹ thuật kiểm thử hộp đen (kiểm thử chức năng) và kỹ thuật kiểm thử hộp trắng( kiểm thử cấu trúc)
2.4.1 Kỹ thuật kiểm thử hộp đen
Kỹ thuật kiểm thử hộp đen (Black- box testing) còn được gọi là kiểm thử hướng dữ liệu (data – driven) hay là kiểm thử hướng vào ra (input/output driven) nhằm mục tiêu tìm ra các lỗi về chức năng, khả năng sử dụng, các lỗi về giao
Trang 18diện, lỗi truy cập dữ liệu bên ngoài, lỗi khởi tạo, hay kết thúc và lỗi liên quan tới các yêu cầu phi chức năng khác Với kỹ thuật kiểm thử hộp đen, người kiểm thử xem hệ thống như là hộp đen, hoàn toàn không quan tâm tới cấu trúc dữ liệu và hành vi bên trong hệ thống, họ chỉ cần quan tâm tới việc phát hiện ra những hành vi không đúng với đặc tả của hệ thống, vì thế dữ liệu đầu vào cho kiểm thử hộp đen chính là tài liệu đặc tả yêu cầu của phần mềm Kiểm thử hộp đen thường được áp dụng trong các giai đoạn sau của kiểm thử hệ thống do nó không cần quan tâm tới cấu trúc, mã nguồn của hệ thống, vì thế chúng ta không thể mong đợi khả năng kiểm tra hết tất cả các lỗi trong hệ thống nhờ áp dụng kỹ thuật này Một số kỹ thuật thường được áp dụng trong kiểm thử hộp đen như:
Phân hoạch tương đương: là kỹ thuật phân chia miền giá trị đầu
vào(input domain) thành lập các miền con dùng cho việc lựa chọn dữ liệu kiểm thử Mỗi miền con được gọi là phân hoạch, dữ kiệu trong cùng một phân hoạch thì có ảnh hưởng tới hệ thống như nhau khi kiểm thử, có thể là cùng hợp lệ hoặc cùng không hợp lệ, vì thế, ta thường lựa chọn ít nhất một tài nguyên điển hình trong từng phân hoạch để làm dữ liệu đầu vào cho việc kiểm thử Ưu điểm của phương pháp phân hoạch tương đương chính là giảm thiểu số lượng dữ liệu cần kiểm tra đối với hệ thống mà vẫn bao phủ được một miền dữ liệu đầu vào khổng
lồ, nó không giới hạn điều kiện vào, chính vì thế ta cũng có thể sử dụng kỹ thuật này cho miền giá tri đầu ra
Phân tích giá trị biên: là kỹ thuật lấy ý tưởng dựa trên phân hoạch tương
đương, sau khi phân hoạch miền dữ liệu đầu vào thành các miền con, ta lựa chọn những dữ liệu ở gần biên (cả trong và ngoài lớp tương đương) làm dữ liệu kiểm thử Từ những dữ liệu kiểm thử đó ta có thể tìm ta được những lỗi gây ra bởi việc thực thi biên chưa chính xác Trên thực tế, các nhà thiết kế cũng như người lập trình thường có xu hướng bỏ qua các điều kiện biên, vì thế khả năng lỗi xuất hiện ở gần biên là rất lớn
Bảng quyết định: do nhược điểm chính của kiểm thử dựa vào các lớp
tương đương là nó chỉ có khả năng kiểm tra được từng dữ liệu đầu vào riêng lẻ
mà không kiểm tra được các một tổ hợp các điều kiện phức tạp Vì thế, ta có thể
sử dụng bảng quyết định để xử lý nhiều dữ liệu đầu vào của nhiều lớp tương đương khác nhau cùng lúc Cấu trúc chung của bảng quyết định thường bao gồm tập các điều kiện (nguyên nhân), tập các ảnh hưởng (hệ quả) và tập các quy tắc
Dữ liệu kiểm thử sẽ được lựa chọn theo từng điều kiện của bảng và kết quả thực
tế sẽ được đối chiếu với kết quả mong muốn Chính vì thế mà mỗi quy tắc trong bảng quyết định sẽ tương ứng với một dữ liệu đầu vào cho kiểm thử
Trang 19 Kiểm thử ngẫu nhiên: là phương pháp lựa chọn ngẫu nhiên dữ liệu đầu
vào cho kiểm thử từ miền giá trị Do dữ liệu đầu vào cho kiểm thử được lựa chọn ngẫu nhiên nên ta sẽ không xác định được dữ liệu đầu ra mong muốn, chính vì thế kỹ thuật này đòi hỏi những chiến lược kiểm thử tốt để có thể đánh giá được độ chính xác của kết quả kiểm thử thông qua việc so sánh với đặc tả hệ thống Ưu điểm của kiểm thử ngẫu nhiên chính là khả năng dễ dàng ước đoán được độ tin cậy của phần mềm dựa vào đầu ra của kiểm thử
Đoán lỗi: là kỹ thuật chủ yếu dựa vào kinh nghiệm của người kiểm thử để
dự đoán loại, vị trí có thể xảy ra lỗi trong hệ thống Kỹ thuật này rất hiệu quả đối với những loại lỗi không thể bao phủ được
2.4.2 Kỹ thuật kiểm thử hộp trắng
Kỹ thuật kiểm thử hộp trắng (white-box testing) còn gọi là kiểm thử hộp thủy tinh (glass-box testing) hay còn gọi là kiểm thử hộp trong suốt (clear-box testing) cho phép kiểm tra cấu trúc bên trong của hệ thống, đảm bảo tất cả các câu lệnh, các biểu thức điều kiện được kiểm tra ít nhất một lần Cũng giống như việc kiểm tra dữ liệu đầu vào trong kiểm thử hộp đen, việc kiểm tra các đường dẫn trong kiểm thử hộp trắng cũng phải được tiến hành một cách triệt để Tuy nhiên, điều đó là hoàn toàn không khả thi, do số lượng đường dẫn logic bên trong mỗi chương trình nếu chứa vòng lặp có thể là vô cùng lớn
Việc tiến hành kỹ thuật kiểm thử hộp trắng phải đảm bảo được một số nguyên tắc như mọi đường dẫn độc lập phải được thử hiện ít nhất một lần, tại mỗi biểu thức điều kiện, các giá trị true, false cũng phải được thể hiện ít nhất một lần, các vòng lặp cùng cần được kiểm tra tại các giá trị biên và các giá trị trung gian, trong phạm vị hoạt động của chúng và cuối cùng là mọi cấu trúc dữ liệu bên trong phải đảm bảo tính đúng đắn, chính xác
Kiểm thử hộp trắng thường tập trung vào hai khía cạnh chính là kiểm thử luồng điều khiển và kiểm thử luồng dữ liệu Kiểm thử luồng điều khiển hướng tới việc thực thi chương trình, đi từ lời chỉ dẫn này cho tới lời chỉ dẫn khác Luồng dữ liệu liên quan tới sự thay đổi giá trị của các biến hay các hằng số Quá trình tiến hành kiểm thử hộp trắng thường được thực hiện ngay tại mỗi bộ chương trình riêng biệt mà người lập trình viết ra
2.5 Ca kiểm thử và thiết kế ca kiểm thử
2.5.1 Định nghĩa ca kiểm thử
Có nhiều khái niệm về ca kiểm thử khác nhau dựa vào quy mô, tổ chức Tuy nhiên, chúng đều có đặc điểm chung như là bao gồm đầu vào, đầu ra mong muốn và các điều kiện thực thi Theo tài liệu thuật ngữ của ISTQB về kiểm thử thì ca kiểm thử là một tập hợp các giá trị đầu vào, các điều kiện tiên quyết, điều
Trang 20kiện thực thi, kết quả mong đợi, các điều kiện kết thúc Nó được xây dựng cho mục đích kiểm thử riêng biệt ví dụ như thực hiện một đường dẫn chương trình riêng biệt hoặc để kiểm tra lại đúng với yêu cầu của đặc tả yêu cầu hay chưa
Dữ liệu đầu ra mong muốn sau khi thực thi chương trình là một thực thể phức tạp, nó có thể bao gồm các giá trị như: giá trị được sinh ra từ chương trình, các trạng thái thay đổi của chương trình, các trạng thái thay đổi của cơ sở dữ liệu… và được xác định trong quá trình thiết kế ca kiểm thử nhờ việc hiểu đặc tả yêu cầu của chương trình
2.5.2 Tiêu chuẩn thiết kế ca kiểm thử
Trong kiểm thử phần mềm, một trong những vấn đề quan trọng nhất chính
là việc thiết kế ra các ca kiểm thử có khả năng tìm ra được nhiều lỗi nhất mà chi phí rẻ nhất Thông thường, phương pháp có khả năng tìm ra nhiều lỗi nhất có thể chính là kiểm tra tất cả các đầu vào của chương trình, tuy nhiên đó là điều không thể do những ràng buộc về mặt thời gian, chi phí và nhân lực Có một vài tiêu chuẩn thiết kế ca kiểm thử thường được áp dụng trong kiểm thử phần mềm:
Bảng 2.1: Các tiêu chuẩn thiết kế ca kiểm thử
Phân lớp tương đương Bao phủ lệnh
Phân tích giá trị biên Bao phủ nhánh(quyết định)
Bảng quyết định Bao phủ điều kiện
Trong kỹ thuật kiểm thử hộp trắng, tiêu chuẩn thiết kế ca kiểm thử bao phủ câu lệnh phải đảm bảo mọi câu lệnh trong chương trình được thực hiện ít nhất một lần Bao phủ lệnh hoàn toàn là tiêu chuẩn bao phủ yếu nhất trong kiểm thử chương trình Việc lựa chọn đường đi trong tiêu chuẩn này có thể tuân theo một số quy tắc như lựa chọn đường đi ngắn nhất, lựa chọn đường đi có chiều dài tăng dần, có thể duyệt qua vòng lắp một vài lần nếu cần thiết Tiêu chuẩn bao phủ điều kiện phải đảm bảo rằng mỗi điều kiện trong một quyết định phải được duyệt qua ít nhất một lần Còn trong tiêu chuẩn bao phủ đa điều kiện, tất cả những sự kết hợp của các kết quả điều kiện có thể có tại mỗi quyết định và các điểm vào phải được duyệt qua ít nhất một lần
2.5.3 Thiết kế ca kiểm thử trong kiểm thử luồng điều khiển
Đồ thị luồng điều khiển( Control flow graph-CFG)
Đồ thị luồng điều khiển là sự miêu tả đồ họa của một bộ chương trình Có ba
ký kiệu chính thường được sử dụng trong đồ thị luồn điều khiển là hình chữ nhật
Trang 21miêu ra các biểu thức toán học, mỗi biểu thức tính toán được gán nhãn bằng một
số nguyên, hình thoi miêu tả các quyết định với hai nhánh tương ứng các quyết định đúng, sai, hình tròn miêu tả điểm hợp nhất của các quyết định Đồ thị luồng điều khiển có thể xây dựng một cách thủ công hoặc trình biên dịch đã được thiết lập để tự động sinh ra từ bộ chương trình
Đường đi trong đồ thị luồng điều khiển
Đường đi (Execution path) trong đồ thị luồng điều khiển là một chuỗi các biểu thức tính toán, các điểm quyết định từ điểm đầu vào cho tới điểm đầu ra Theo T J McCabe [13], số đường đi ứng với đồ thị luồng điều khiển được tính bằng một trong các phương pháp sau:
- Số cạnh – số đỉnh + 2
- Số đỉnh quyết định + 1 Sau khi có được các đường đi của đơn vị chương trình cần kiểm thử, với mỗi đường đi, chúng ta sẽ sinh một ca kiểm thử tương ứng
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
đi trong đồ thị luồng điều khiển của đơn vị phần mềm cần kiểm thử đều chạy đúng Tuy nhiên để đạt được mục tiêu này thì công sức và thời gian bỏ ra rất lớn Vì vậy, nên chọn lọc và kiểm thử số test case tối thiểu mà kết quả độ tin cậy tối đa Nhưng làm sao xác định được số test case tối thiểu nào có thể đem lại kết quả có độ tin cậy tối đa Phủ kiểm thử là một khái niệm liên quan chặt chẽ đến vấn đề trên Phủ kiểm thử (Coverage) : là tỉ lệ các thành phần thực sự được kiểm thử so với tổng thể sau khi đã kiểm thử các test case được chọn Phủ càng lớn thì
độ tin cậy càng cao[1] Bộ test case cần thỏa mãn một số tiêu chuẩn lựa chọn đường đi để đạt mức phủ cần thiết
Tiêu chuẩn lựa chọn đường đi
Trong mỗi đồ thị luồng điều khiển, có thể có rất nhiều đường đi khác nhau
vì thế người kiểm thử cần phải có phương pháp lựa chọn một số ít các đường đi trong chương trình mà vẫn phát hiện lỗi trong mã nguồn một cách hiệu quả Một quy tắc thường được áp dụng trong việc tiến hành kiểm thử dựa vào đồ thị luồng điều khiển như tất cả lời khởi tạo trong chương trình đều được thực hiện ít nhất một lần, không tạo ra nhiều dữ liệu đầu vào kiểm thử cho những đường đi được thực hiện nhiều lần Tuy nhiên, nếu mỗi lần thực thi đường đi của chương trình lại cập nhật lại trạng thái của hệ thống ví dụ như trạng thái cơ sở dữ liệu của hệ thống thì chúng ta nên tạo ra nhiều dữ liệu đầu vào kiểm thử cho những lần thực thi đó Có một số tiêu chuẩn lựa chọn đường đi như:
Tiêu chuẩn bao phủ các câu lệnh
Trang 22Bộ test case kiểm thử đảm bảo sao cho mỗi lệnh được thực thi ít nhất một lần Trong thiết kế test case ta luôn cố gắng bao phủ tối đa câu lệnh trong mã nguồn với số test case ít nhất có thể Bao phủ câu lệnh sẽ nhận ra các câu lệnh trong một phương thức hay trong một lớp đã được thực thi Đây là một phương pháp đo đơn giản để tìm ra số câu lệnh đã được thực thi trong tổng số các câu lệnh mã nguồn Do đó lợi ích của bao phủ câu lệnh là khả năng tìm ra các dòng code không được thực thi
Tiêu chuẩn cả bao phủ nhánh
Tiêu chuẩn bao phủ nhánh là kiểm thử sao cho mỗi điểm quyết định được thực hiện ít nhất một lần trong cả hai trường hợp true và false Điều kiện không cần chia nhỏ nếu là điều kiện phức tạp gồm nhiều điều kiện con Một nhánh là một kết luận logic của một điều kiện, do vậy bao phủ nhánh đơn giản là đo kết luận logic nào đã được kiểm tra Phương pháp bao phủ này xem xét mã nguồn sâu sắc hơn là phương pháp bao phủ câu lệnh Xác định số nhánh có trong một phương thức là một việc dễ làm Kết luận kiểu boolean hiển nhiên có hai kết luận logic là “true” hoặc “false” do đó chương trình có n điều kiện sẽ có 2nnhánh Phương pháp bao phủ nhánh vẫn bao hàm cả bao phủ câu lệnh tuy nhiên
nó đã loại bỏ được một số hạn chế có ở bao phủ câu lệnh
Tiêu chuẩn bao phủ điều kiện
Giống như bao phủ nhánh, kiểm tra bao phủ điều kiện thiết lập đảm bảo kiểm tra từng điều kiện Tuy nhiên không giống như bao phủ nhánh, bao phủ điều kiện đảm bảo kiểm tra tất cả các điều kiện con của từng điểm quyết định đều được thực hiện ít nhất một lần trong cả trường hợp true và false Với các điều kiện phức tạp (chứa nhiều điều kiện con cơ bản), việc chỉ quan tâm đến giá trị đúng sai là không đủ để kiểm tra tính đúng đắn của chương trình ứng với điều kiện phức tạp này Ví dụ, nếu một điều kiện phức tạp gồm hai điều kiện con cơ bản, chúng ta có bốn trường hợp cần kiểm thử chứ không phải hai trường hợp đúng sai như trong phương pháp bao phủ nhánh Với các hàm hay phương thức
có yêu cầu cao về tính đúng đắn, việc tuân thủ bao phủ điều kiện là hết sức cần thiết Điều kiện để đảm bảo tính phủ này là các điều kiện con thuộc các điều kiện phức tạp tương ứng với các điểm quyết định trong đồ thị luồng điều khiển của phương thức cần kiểm thử đều được thực hiện ít nhất một lần cả hai nhánh đúng và sai
Lựa chọn đường đi kiểm thử vòng lặp
Vòng lặp là nền tảng cho hầu hết các thuật toán được cài đặt trong phần mềm Tuy nhiên, chúng ta thường ít quan tâm đến nó khi thực hiện việc kiểm thử phần mềm Kiểm thử vòng lặp là một kỹ thuật kiểm thử hộp trắng mà tập
Trang 23trung trên tính hợp lệ của các cấu trúc lặp Khi tiến hành kiểm thử các đơn vị chương trình với bao phủ điều kiện thì phương pháp kiểm thử luồng điều khiển không thể kiểm thử các vòng lặp xuất hiện trong các đơn vị chương trình Lý do
là các đường đi sinh ra từ đồ thị luồng điều khiển không chứa các vòng lặp[1] Trong thực tế, lỗi hay xảy ra ở các vòng lặp Vì lý do này, chúng ta cần sinh thêm các ca kiểm thử cho các vòng lặp nhằm giảm tỷ lệ lỗi của các chương trình Việc xây dựng các trường hợp kiểm thử cho mỗi loại vòng lặp cần thực hiện như sau:
Vòng lặp đơn
Vòng lặp mà trong thân không xuất hiện thêm vòng lặp nào khác gọi là 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 tra mỗi điều kiện sau:
có thể tạo ra một số không thực tế các trường hợp kiểm thử
Trang 24Hình 2.3: Sơ đồ mô tả vòng lặp lồng nhau
Chính vì vậy, một cách tiếp cận đệ qui như sau sẽ giảm bớt số trường hợp kiểm thử:
- 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 liền kề
Nếu các vòng lặp liền kề 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
Các bước sinh dữ liệu kiểm thử
Sau khi xác định được đường đi dựa vào đồ thị luồng điều khiển, ta tiến hành sinh dữ liệu đầu vào cho hoạt động kiểm thử Với bộ dữ liệu đầu vào, chương trình sẽ thực thi theo đường đi đã lựa chọn Trong quá trình sinh dữ liệu kiểm thử, ta cần xác định một số tham số như:
Trang 25- Vector đầu vào là một tập dữ liệu đầu vào cho thủ tục, bao gồm các tham
số đầu vào cho thủ tục, giá trị các biến toàn cục, hằng số, các file, kết nối mạng,…
- Predicate: biểu thức điều kiện tại các điểm quyết định
- Xác định đường đi- path predicate expression: chứa tập các biểu thức điều kiện cùng với giá trị của biểu thức đó và thứ tự các biểu thức điều kiện gắn với một đường đi xác định nào đó
Sau khi xác định được biểu thức chỉ định đường đi, hoàn toàn có thể sinh ra dữ liệu đầu vào cho kiểm thử thông qua chuỗi biểu thức đã xây dựng
Trang 26CHƯƠNG 3 – CÔNG CỤ KIỂM CHỨNG JAVA PATHFINDER
Java PathFinder (JPF)[15,16] là một hệ thống để kiểm chứng mã thực thi bytecode của các chương trình Java Nó khảo sát tất cả các đường thực thi tiềm năng của một chương trình để tìm ra các vi phạm của các thuộc tính như khóa chết (deadlock) hay các ngoại lệ không được xử lý (unhanled exceptions) Không giống như các công cụ gỡ lỗi truyền thống, JPF thông báo đầy đủ đường thực thi dẫn đến một một lỗi
JPF hỗ trợ nhiều tính năng mở rộng trong đó JPF có thể ghi lại vết của ngăn xếp (stack trace) khi thực thi một chương trình java Từ đó lập trình viên có thể
dễ dàng phát hiện và sửa lỗi chương trình một cách nhanh chóng, giúp làm giảm chi phí phát triển và bảo trì phần mềm
3.1 Lịch sử của Java PathFinder
JPF hoàn toàn là một chương trình Java có thể được chạy như là một công
cụ dòng lệnh độc lập hoặc được nhúng vào các hệ thống khác như các môi trường phát triển Lịch sử phát triển của JPF như sau:
- Năm 1999 : JPF1 là Java-to- Promela
- Năm 2000: JPF2 là công cụ kiểm chứng mô hình được tùy biến có thể hiểu mã bytecode của Java và sử dụng mã bytecode một cách trực tiếp
- Năm 2003: thiết kế và thực thi cấu trúc mở rộng
- Năm 2005: mã nguồn của JPF được mở ra cho cộng đồng phát triển,
- Năm 2006: JPF4 với bộ sinh hợp nhất và máy thực thi mới
3.2 Những gì có thể được kiểm chứng bởi Java PathFinder
JPF là một nền tảng có thể mở rộng được Do đó nhiều dự án đã được triển khai nhằm mở rộng những tính năng cho JPF Hiện tại, các mở rộng của JPF bao gồm: kiểm chứng mô hình giao diện người dùng (User Interface Model Checking), sinh dữ liệu kiểm thử ký hiệu (Symbolic Test Data Generation), sinh kiểm thử luồng an toàn ký hiệu (Symbolic Threadsafety Test Generation), khung làm việc kiểm chứng cấu thành (Compositional Verification Framework), kiểm chứng đặc tính số (Numeric Property Verification), kiểm chứng mô hình biểu đồ trạng thái UML (UML State Chart Model Checking) Java Pathfinder có thể tìm các lỗi khóa chết (deadlock) và các ngoại lệ không được xử lý (unhandled exception) như NullPointerExceptions và AssertionErrors, tuy nhiên người dùng còn có thể tự cung cấp các lớp hoặc viết các phần mở rộng để thực hiện kiểm chứng các thuộc tính khác JPF có thể thực hiện mô phỏng không tất định (non-determinism) Mô phỏng không tất định yêu cầu hệ thống sinh tất cả các lựa chọn có thể cho tất cả các trạng thái của hệ thống Hệ thống này có hai khả năng:
Trang 27backtracking và state matching Hai cách để tạo ra mô phỏng không tất định trong JPF là quay lui (backtracking) và so khớp trạng thái (state matching)
3.3 Kiến trúc của Java PathFinder
JPF [15,16] là một máy ảo Java (JVM) được sử dụng để phát hiện lỗi (bugs) trong các chương trình Java Có thể sử dụng JPF một cách linh hoạt dưới dạng giao diện dòng lệnh hoặc tích hợp vào trong các môi trường phát triển ứng dụng Java như Netbean, Eclipse JPF là một ứng dụng Java mã nguồn mở cho phép ta cấu hình để sử dụng nó theo các cách khác nhau và mở rộng nó
JPF được thiết kế xung quanh hai khái niệm trừu tượng chính: JPF được thiết kế gồm hai thành phần chính đó là: đối tượng máy ảo (JVM) và đối tượng tìm kiếm (Search Object)
Hình 3.1: Kiến trúc JPF
Máy ảo (JVM):
Máy ảo JVM của JPF là một bộ sinh trạng thái bằng việc thực thi các chỉ thị bytecode, nó có khả năng quản lý các trạng thái: quay lui, khớp trạng thái và máy ảo lưu trữ trạng thái Có 3 phương thức JVM chính hợp tác giữa máy ảo và tìm kiếm (VM-Search):
Trang 28 forward() – sinh trạng thái tiếp theo, thông báo nếu trạng thái sinh
ra là nút lá Nếu đúng lưu trữ vào một ngăn xếp backtrack phục vụ phục hồi hiệu quả
backtrack() – phục hồi trạng thái cuối trong ngăn xếp quay lui
restoreState() – phục hồi một trạng thái tùy ý
Máy ảo JPF được cài đặt tuân theo các đặc tả về máy ảo Java nhưng không giống như các máy ảo Java chuẩn khác máy ảo JPF có khả năng lưu trữ trạng thái (state storing), ghi nhớ trạng thái (state matching) và quay lui trong quá trình thực thi chương trình
Quay lui (backtracking) có nghĩa là JPF có thể phục hồi lại trạng thái thực thi lúc trước để xem có còn những sự lựa chọn thực thi khác
Ghi nhớ trạng thái (state matching) có nghĩa là đánh dấu các trạng thái đã thám hiểm mà không còn cần thiết nữa giúp JPF tránh được những sự thực thi không cần thiết Trạng thái thực thi của một chương trình bao gồm heap và ngăn xếp tiến trình Trong khi thực thi JPF kiểm tra mọi trạng thái mới có phải là các trạng thái đã được ghi nhớ Nếu các trạng thái đã được ghi nhớ JPF sẽ không tiếp tục đi theo đường đi thực thi hiện hành và quay lui tới trạng thái gần nhất
mà chưa được thám hiểm
Đối tƣợng tìm kiếm (Search Object): là một cấu hình chiến lược tìm kiếm
đối tượng theo hướng thực thi trong máy ảo Nói cách khác, đối tượng tìm kiếm lựa chọn các trạng thái mà từ đó JVM nên xử lý bằng cách trực tiếp JVM sinh ra trạng thái tiếp theo hoặc quay lại trạng thái được tạo ra trước đó Một khả năng
quan trọng của đối tượng tìm kiếm là nó có thể đánh giá đối tượng thuộc tính,
chẳng hạn như: xác định một thuộc tính là không phải deadlock - phương thức
NotDeadlocked, hoặc đảm bảo rằng các thuộc tính không vi phạm sự khẳng
định – phương thức NoAssertionsViolatedProperty
Search Object là bộ điều khiển của VM Search Object điều khiển việc thực thi trong JVM Search Object chịu trách nhiệm chọn lựa trạng thái để VM tiếp tục quá trình thực thi bằng việc điều khiển VM để sinh ra trạng thái tiếp theo (foward) hoặc quay lui về trạng thái đã được tạo lúc trước Theo mặc định, kỹ thuật tìm kiếm theo chiều sâu được sử dụng Một kỹ thuật tìm kiếm dựa trên hàng đợi ưu tiên (tìm kiếm kinh nghiệm) cũng được cài đặt và ta có thể cấu hình
để sử dụng nó Phương thức tìm kiếm được cài đặt với một vòng lặp và chỉ dừng lại khi không gian trạng thái đã được thám hiểm hết hoặc tìm ra một sự vi phạm
Trang 29Với những chương trình lớn và phức tạp thì không gian trạng thái trong quá trình thực thi chương trình là rất lớn Việc lưu trữ và thám hiểm tất cả các trạng thái không phải lúc nào cũng đạt được JPF sử dụng các kỹ thuật khác nhau để giải quyết vấn đề này đó là các chiến lược tìm kiếm, các kỹ thuật để giảm thiểu
số trạng thái chương trình và việc lưu trữ các trạng thái Có các chiến lược tìm kiếm khác nhau được sử dụng như tìm kiếm theo chiều sâu (Depth-First Search), theo chiều rộng (Breadth-First Search), tìm kiếm kinh nghiệm
3.4 Một số phần mở rộng của Java PathFinder
JPF cung cấp một số phần mở rộng như sau:
Bytecode Factories: cho phép thay đổi ngữ nghĩa trong thực thi chỉ thị bytecode
Listeners: theo dõi và kiểm soát thực thi chương trình JPF
Report: định dạng, hiển thị các đầu ra
3.4.1 Bộ tạo chỉ thị (Bytecode Factory)
Hình 3.2: Bộ tạo chỉ thị Bytecode Factory
Trong JPF có các lớp biểu thị cho tất cả các chỉ thị bytecode được cài đặt,
để thông dịch các chỉ thị bytecode Bộ tạo chỉ thị (Instuction Factory) cho phép thay thế và mở rộng ngữ nghĩa thực thi cụ thể của bytecode bằng ngữ nghĩa thực
Trang 30thi tượng trưng Các thông tin tượng trưng được lưu trữ trong các thuộc tính (attributes) kết hợp với dữ liệu chương trình (fields, stack operands, local variables) và được cập nhật trong quá trình thực thi Lớp InstructionFactory là giao diện để tạo các đối tượng Instruction bao gói việc thực thi các chỉ thị
bytecode Khi một chỉ thị bytecode trong tập tin class được tải vào trong máy
ảo JPF để xử lý thì đối tượng Instruction sẽ được tạo ra Nói cách khác, khi một
tập tin class được đọc thì một mảng các đối tượng Instruction sẽ được tạo ra
Với mỗi phương thức được thực thi, JPF quản lý một đối tượng đó là MethodInfo Đối tượng MethodInfo lưu trữ các chỉ thị bytecode như là mảng các đối tượng Instruction Các đối tượng Instruction có phương thức execute()
để thực hiện việc thực thi chỉ thị bytecode tương ứng Chế độ thực thi mặc định trong JPF là chế độ thực thi cụ thể Với giao diện InstructionFactory, ta có khả năng ghi đè các lớp chỉ thị mà cài đặt việc thông dịch các chỉ thị bytecode để thay đổi chế độ thực thi trong JPF
JPF sử dụng mẫu thiết kế abstract factory[14] để khởi tạo các đối tượng Instruction Lớp SymbolicInstructionFactory chứa các chỉ thị bytecode theo ngữ nghĩa thực thi tượng trưng Lớp SymbolicInstructionFactory ủy quyền (delegate) tới lớp cha DefaultInstructionFactory chứa các chỉ thị bytecode theo nghĩa thực thi cụ thể
Trang 31không yêu cầu bất kỳ sửa đổi nào trong nhân của JPF Listeners được thực thi cùng cấp với JPF
Để sử dụng các tính năng mở rộng của Listener người dùng có thể cấu hình các thuộc tính trong file *.jpf hoặc cấu hình khi thực thi dòng lệnh Các ứng dụng có thể sử dụng chú thích @JPFConfig để xác định rõ thuộc tính cho JPF Listeners
Các loại Listeners
Search Listener và VM Listener: Chúng được sử dụng để quan sát việc
thực thi mã bên trong JPF mà không cần cài đặt lại các lớp của JVM và Search
Hình 3.4: Các loại Listener
Search Listener: Được sử dụng để theo dõi quá trình tìm kiếm không gian trạng thái, nó được đăng ký tới các sự kiện của đối tượng Search:
public interface SearchListener {
void stateAdvanced (Search search);
//state was backtracked one step
//a previously generated state was restored
void stateRestored (Search search);
// JPF encountered a property violation
//a search loop was started
void searchStarted (Search search);
Trang 32// the search loop was finished
void searchFinished (Search search);
}
Các thuộc tính được sử dụng thường xuyên nhất là:
+ stateAdvanced - để lưu trữ bổ sung, thông tin trạng thái backtrackable trong một mảng
+ stateBacktracked - để khôi phục lại thông tin trạng thái bổ sung
+ searchFinished - để xử lý kết quả thu được
VM Listener : Được đăng ký tới các sự kiện của đối tượng VM trong quá trình thực thi một chương trình VM Listener được sử dụng để quản lý việc thực thi các chỉ thị bytecode:
// VM has executed next instruction
Trang 33+ instructionExecuted: thông báo sau khi thực hiện, phù hợp để theo dõi kết quả thực hiện (lời gọi đến phương thức, các giá trị gán, kết quả của các nhánh, …)
PropertyListenerAdapter được sử dụng trong trường hợp Listeners khi triển khai các thuộc tính, tức là có thể chấm dứt quá trình tìm kiếm Một ví dụ nổi bật của thể loại này là PreciseRaceDetector
ListenerAdapter là bộ chuyển đổi cho SearchListener, VMListener và PublisherExtension Đây là nơi chủ yếu được sử dụng để thu thập thông tin trong quá trình thực hiện JPF (ví dụ sử dụng lớp CoverageAnalyzer hay DeadlockAnalyzer)
3.4.3 Hệ thống báo cáo (The Report)
Hệ thống báo cáo (The Report) [15,16] có chức năng nhận kết quả đầu ra, định dạng và hiển thị các kết quả khi thực thi JPF như báo cáo vi phạm thuộc tính, vết chương trình, các thống kê… Đây là một phần quan trọng nhất của giao diện người dùng JPF, nó có thể hiển thị các định dạng đầu ra khác nhau như: tệp văn bản, màn hình console, XML, các lời gọi API Tùy thuộc vào ứng dụng và
dự án, người dùng có thể thay đổi cách hiển thị bằng việc cấu hình lại các thuộc tính Hệ thống báo cáo của JPF bao gồm 3 thành phần:
- Lớp Reporter
- Các đối tượng Publisher
- Các đối tượng PublisherExtension
Tất cả các lớp chính liên quan và các giao diện nằm trong gói gov.nasa.jpf.report Trong đó:
- Đối tượng Reporter có nhiệm vụ thu gom dữ liệu Nó quản lý và thông báo cho các phần mở rộng của Publisher khi dữ liệu đầu ra đã đạt tới ngưỡng
- Đối tượng Publisher có nhiệm vụ ghi dữ liệu từ Reporter ra một định dạng nào đó (text, xml, html,…) Publisher thông dụng nhất là ConsolePublisher, nó
có chức năng ghi dữ liệu ra dạng text, console hoặc ghi ra file
- Đối tượng PublisherExtension sẽ được đăng ký với đối tượng Publisher khi chương trình bắt đầu chạy Mục đích để có thể định nghĩa một PublisherExtension là kế thừa đối tượng ListenerAdaper, đăng ký Publisher Extension này với JPF trong hàm khởi tạo của nó
- Một đối tượng khá quan trọng trong JPF đó chính là Config, đây là đối tượng lưu trữ tất cả các thuộc tính (properties) cấu hình của JPF Các thuộc tính
của JPF sẽ được định nghĩa trong file jpf.properties và *.jpf Dưới đây là một số
thuộc tính quan trọng của Report JPF:
Trang 34 report.class=gov.nasa.jpf.report.Reporter: chỉ định Report cho chương trình
report.publisher=console,xml: chỉ định có thể báo cáo ở dạng console hoặc xml
Hình 3.5: Hệ thống báo cáo Report
report.console.class=gov.nasa.jpf.report.ConsolePublisher:chỉ định đối tượng để báo cáo ghi vết là ConsolePublisher
report.console.property_violation=error,trace,snapshot: sẽ ghi thông tin ở dạng error, stack trace vào snapshot
report.console.file=My_JPF_report: chỉ định đường dẫn chứa file vết của JPF
3.5 Đo độ phủ trong kiểm thử đơn vị sử dụng JPF CoverageAnalyzer
JPF là công cụ kiểm chứng phần mềm hỗ trợ nhiều tính năng phục vụ cho việc kiểm chứng Một trong số đó chính là đo độ phủ của các ca kiểm thử với phương thức được thực thi Phần mở rộng Listener là một phần mở rộng quan trọng, nó có nhiệm vụ theo dõi mã nguồn và kiểm soát thực thi chương trình Để thực hiện đo độ phủ của bộ các ca kiểm thử chúng ta có thể sử dụng một tính năng trong phần mở rộng này cụ thể là sử dụng lớp CoverageAnalyzer Lập trình viên sẽ cấu hình file config nhằm gọi sử dụng đối tượng của lớp