Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử

Gỡ rối

Debug

▪ Gỡ rối là gì?

▫ Khi chương trình bị lỗi, gỡ rối là các công việc cần làm để làm

cho chương trình dịch thông, chạy thông

▫ Thật không may, gỡ rối luôn là thao tác phải làm khi lập trình,

thao tác này rất tốn kém

▪ Cách tốt nhất vẫn là phòng ngừa

▫ Khi bắt đầu gỡ rối chương trình, bạn đã biết là chương trình

không chạy.

▫ Nếu bạn biết lý do tại sao chương trình không chạy, bạn có thể

sửa được chương trình cho nó chạy

▫ Nếu bạn hiểu chương trình của bạn, bạn sẽ có ít sai lầm và dễ

dàng sửa chữa sai sót hơn. Bí quyết là viết mã đơn giản, hiệu quả,

chú thích lý.hợpGỡ rối

Debug

▪ Đối với mã nguồn, tiêu chí nào quan trọng hơn: rõ ràng hay

chính xác?

▫ Nếu mã nguồn rõ ràng, bạn có thể làm cho chương trình trở nên

chính xác.

▫ Bạn có chắc là làm cho chương trình trở nên chính xác nếu nó

không rõ ràng hay không?

▪ Nếu chương trình được thiết kế với cấu trúc tốt, được viết

bằng phong cách lập trình tốt và áp dụng các kỹ thuật viết

chương trình hiệu quả, bẫy lỗi thì chi phí cho việc gỡ rối sẽ

được giảm thiểu.

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 1

Trang 1

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 2

Trang 2

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 3

Trang 3

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 4

Trang 4

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 5

Trang 5

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 6

Trang 6

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 7

Trang 7

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 8

Trang 8

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 9

Trang 9

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử trang 10

Trang 10

Tải về để xem bản đầy đủ

pdf 94 trang duykhanh 9160
Bạn đang xem 10 trang mẫu của tài liệu "Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

Tóm tắt nội dung tài liệu: Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử

Bài giảng Kỹ thuật lập trình - Chương 9: Gỡ lỗi và kiểm thử
hiều so với statement testing 
 ▫ Với các chương trình đơn giản, có thể liệt kê các nhánh đường dẫn 
 xuyên suốt code 
 ▫ Ngược lại, bằng các đầu vào ngẫu nhiên tạo các đường dẫn theo 
 chương trình 
 Kiểm chứng 
 lộ trình 
▪ Example pseudocode: if (condition1) 
 statement1; 
 else 
 Path testing: statement2; 
 Cần đảm bảo tất cả các đường if (condition2) 
 dẫn được thực hiện statement3; 
 else 
 statement4; 
▪ Đòi hỏi 4 tập dữ liệu: 
 ▫ condition1 là true và condition2 là true 
 ▫ condition1 là true và condition2 là false 
 ▫ condition1 là false và condition2 là true 
 ▫ condition1 là false và condition2 la false 
▪ Chương trình thực tế => bùng nổ các tổ hợp!!! 
 Kiểm chứng 
 lộ trình 
 1 
(1) input(A,B) A>0 
 if (A>0) F T 
(2) Z = A; 3 2 
 B>0 
 else T 
 F 
(3) Z = 0; 4 
 if (B>0) 5 
(4) Z = Z+B; 
(5) output(Z) 
 What is the path condition for path ? 
 (A>0) && (B 0) 
 Kiểm chứng 
 lộ trình 
 1 
(1) input(A,B) A>B 
 T 
 if (A>B) 
 F 2 
(2) B = B*B; B<0 
 if (B<0) F T 
(3) Z = A; 4 3 
 else 5 
(4) Z = B; 
 What is the path condition for path ? 
(5) output(Z) 
 (A>B) && (B<0) 
 Kiểm chứng 
 lộ trình 
 1 
(1) input(A,B) A>B 
 T 
 if (A>B) 
 F 2 
(2) B = B*B; B<0T 
 F 
 T 
 if (B<0) 4 3 
(3) Z = A; 5 
 else 
(4) Z = B; 
 What is the path condition for path ? 
 2
(5) output(Z) (A>B) Л (B<0) (B <0) = FALSE 
 Kiểm chứng 
 tải 
 (4) Stress testing 
 “Tiến hành thử nghiệm để đánh giá một hệ thống hay thành phần 
 tại hoặc vượt quá các giới hạn của các yêu cầu cụ thể của nó” 
 ‒ Glossary of Computerized System and Software Development Terminology 
▪ Phải tạo 
 ▫ Một tập lớn đầu vào 
 ▫ Các đầu vào ngẫu nhiên (binary vs. ASCII) 
▪ Nên dùng máy tính để tạo đầu vào 
 Kiểm chứng 
 tải 
▪ Example program: #include 
 int main(void) { 
 Stress testing: Phải char c; 
 cung cấp random while ((c = getchar()) != EOF) 
 putchar(c); 
 (binary and ASCII) return 0; 
 inputs } 
▪ Mục tiêu: Copy tất cả các ký tự từ stdin vào stdout; nhưng 
lưu ý bug!!! 
▪ Làm việc với tập dữ liệu ASCII chuẩn (tự tạo) 
▪ Máy tính tự tạo ngẫu nhiên tập dữ liệu dạng 255 (decimal), 
hay 11111111 (binary), và EOF để dừng vòng lặp 
 Kiểm chứng 
 tải 
▪ Example program: #include 
 int main(void) { 
 short charCount = 0; 
 while (getchar() != EOF) 
 Stress testing: Phải charCount++; 
 printf("%hd\n", charCount); 
 cung cấp số lượng return 0; 
 input rất lớn } 
▪ Mục tiêu: Đếm và in số các kỹ tự trong stdin 
▪ Làm việc với tập dữ lieu có kích thước phù hợp 
▪ Sẽ có lỗi với tập dữ liệu do máy tạo chứa hơn 32767 
characters 
Các KIỂM THỬ 
kỹ thuật TRONG 
kiểm thử 
 Kiểm thử trong 
 Internal testing 
▪ Internal testing: Thiết kế chương trình để chương trình tự 
 kiểm thử 
▪ Internal testing techniques 
 (1) Kiểm tra bất biến - Testing invariants 
 (2) Kiểm tra các thuộc tính lưu trữ -Verifying 
 conservation properties 
 (3) Kiểm tra các giá trị trả về - Checking function 
 return values 
 (4) Tạm thay đổi code - Changing code temporarily 
 (5) Giữ nguyên mã thử nghiệm - Leaving testing code 
 intact 
 Kiểm tra tính 
 bất biến 
 (1) Testing invariants 
 ▫ Thử nghiệm các đk trước và sau 
 ▫ Vài khía cạnh của cấu trức dữ liệu không đc thay đổi 
 ▫ 1 hàm tác động đến cấu trúc dữ liệu phải kiểm tra các bất biến ở đầu 
 và cuối nó 
 ▫ Ví dụ: Hàm “doubly-linked list insertion” 
 ▸ Kiểm tra ở đầu và cuối 
Xoay doubly-linked list 
Khi node x trỏ ngược lại node y, thì liệu node y có trỏ ngược lại node x? 
 ▫ Ví dụ: “binary search tree insertion” function 
 ▸ Kiểm tra ở đầu và cuối 
Xoay tree 
Các nodes có còn đc sắp xếp không? 
Kiểm tra tính 
bất biến 
 #ifndef NDEBUG ▪ Có thể dung 
 int isValid(MyType object) { assert() 
 Test invariants here. 
 Return 1 (TRUE) if object passes 
 all tests, and 0 (FALSE) otherwise. 
 } Có thể dùng NDEBUG 
 #endif trong code, giống như 
 assert 
 void myFunction(MyType object) { 
 assert(isValid(object)); 
 Manipulate object here. 
 assert(isValid(object)); 
 } 
Kiểm tra các 
thuộc tính lưu trữ 
▫ Khái quát hóa của testing invariants 
▫ 1 hàm cần kiểm tra các cấu trúc dữ liệu bị tác động tại các điểm 
đầu và cuối 
▫ VD: hàm str_concat() 
 ▸ Tại điểm đầu, tìm độ dài của 2 xâu đã cho; tính tổng 
 ▸ Tại điểm cuối, tìm độ dài của xâu kết quả 
 ▸ 2 độ dài có bằng nhau không ? 
▫ VD: Hàm chèn thêm PT vào danh sách -List insertion function 
 ▸ Tại điểm khởi đầu, tính độ dài ds 
 ▸ Tại điểm cuối, Tính độ dài mới 
 ▸ Độ dài mới = độ dài cũ + 1? 
 Kiểm tra 
 giá trị trả về 
▪ Trong Java và C++ 
 ▫ Phương thức bị phát hiện có lỗi có thể tung ra một “checked 
 exception” 
 ▫ Phương thưc triệu gọi phải xử lý ngoại lệ 
▪ Trong C 
 ▫ Không có cơ chế xử lý exception 
 ▫ Hàm phát hiện có lỗi chủ yếu thông qua giá trị trả về 
 ▫ Người LT thường dễ dàng quên kiểm tra GT trả về 
 ▫ Nói chung là chúng ta nên kiểm tra GT trả về 
Kiểm tra 
giá trị trả về 
▫ VD: scanf() trả về số của các giá trị được đọc 
 Bad code Good code 
 int i; int i; 
 scanf("%d", &i); if (scanf("%d", &i) != 1) 
 /* Error */ 
 Bad code??? Good code, or overkill??? 
 int i = 100; int i = 100; 
 printf("%d", i); if (printf("%d", i) != 3) 
 /* Error */ 
▫ VD: printf() có thể bị lỗi nếu ghi ra file và đĩa bị đầy; Hàm 
này trả về số ký tự được ghi (không phải giá trị) 
Tạm thay đổi 
mã nguồn 
▫ Tạm thay đổi code để tạo ranh giới nhân tạo hoặc stress tests 
▫ VD: chương trình sắp xếp trên mảng 
 ▸ Tạm đặt kích thước mảng nhỏ 
 ▸ chương trình có xử lý tràn số hay không ? 
▫ Viết 1 phiên bản hàm cấp phát bộ nhớ và phát hiện ra lỗi sớm, 
để kiểm chứng đoạn mã nguồn bị lỗi thiếu bộ nhớ 
 void *testmalloc( size_t n) { 
 static int count =0; 
 if (++count > 10) 
 return NULL; 
 else 
 return malloc(n); 
 } 
 Giữ nguyên 
 mã kiểm tra 
 ▫ Để nguyên trạng các đoạn kiểm tra trên code 
 ▫ Có thể khoanh lại = #ifndef NDEBUG  #endif 
 ▫ Kiểm tra với tùy chọn –DNDEBUG gcc 
 ▸ Bặt/Tắt assert macro 
 ▸ Cũng có thể Bật/tắt debugging code 
▪ Cẩn trọng với conflict: 
 ▫ Mở rộng thử nghiệm nội bộ có thể giảm chi phí bảo trì 
 ▫ Code rõ ràng có thể giảm chi phí bảo trì 
 ▫ Nhưng mở rộng thử nghiệm nội bộ có thể làm giảm độ rõ ràng 
 của Code 
Các chiến lược 
kiểm thử 
 Các chiến lược 
 kiểm thử 
Testing strategies 
 (1) Kiểm chứng tăng dần - Testing incrementally 
 (2) So sánh các cài đặt - Comparing implementations 
 (3) Kiểm chứng tự động - Automation 
 (4) Bug-driven testing 
 (5) Tiêm, gài lỗi - Fault injection 
 Kiểm chứng 
 tăng dần 
(1) Testing incrementally 
 ▫ Test khi viết code 
 ▸ Thêm test khi tạo 1 lựa chọn mới - new cases 
 ▸ Test phần đơn giản trước phần phức tạp 
 ▸ Test units (tức là từng module riêng lẻ) trước khi testing toàn hệ 
 thống 
 ▫ Thực hiện regression testing – kiểm thử hồi quy 
 ▸ Xử lý đc 1 lỗi thường tạo ra những lỗi mới trong 1 hệ thống lớn, vì 
 vậy  
 ▸ Phải đảm bảo chắc chắn hệ thống không “thoái lui” kiểu như chức 
 năng trước kia đang làm việc giờ bị broken, nên 
 ▸ Test mọi khả năng để so sanh phiên bản mới với phiên bản cũ 
Kiểm chứng 
tăng dần 
▫ Tạo “giàn giáo” - scaffolds và “mẫu” - stubs để test đoạn code 
mà ta quan tâm 
 Scaffold: Đoạn code 
 Hàm gọi đến code tạm thời gọi đến code 
 mà ta quan tâm Mà ta quan tâm 
 Đoạn code cần quan tâm 
 Stub: Đoạn code 
 Hàm được gọi Hàm được gọi 
 Tạm thời được gọi 
bởi đoạn code cần bởi đoạn code cần 
 Bởi đoạn code cần 
 quan tâm quan tâm 
 Quan tâm 
 So sánh 
 các cài đặt 
(2) Compare implementations 
 ▫ Hãy chắc chắn rằng các triển khai độc lập hoạt động như nhau 
 ▫ Ví dụ: So sánh hành vi của các hàm bạn tạo trong str.h với các 
 hàm trong thư viện string.h 
 ▫ Đôi khi 1 kết quả có thể đc tính bằng 2 cách khác nhau, 1 bài 
 toán có thể giải bằng 2 phương pháp, thuật toán khác nhau. Ta 
 có thể xây dựng cả 2 chương trình, nếu chúng có cùng kết quả thì 
 có thể khẳng định cả 2 cùng đúng, còn kết quả khác nhau thì ít 
 nhất 1 trong 2 chương trình bị sai. 
 Kiểm chứng tự động 
 Automation 
(3) Automation 
 ▫ Là quá trình xử lý 1 cách tự động các bước thực hiện test case = 
 1 công cụ nhằm rut ngắn thời gian kiểm thử. 
 ▫ Ba quá trình kiểm chúng bao gồm : 
 ▸ Thực hiện kiểm chứng nhiều lần 
 ▸ Dùng nhiều bộ dữ liệu nhập 
 ▸ Nhiều lần so sánh dữ liệu xuất 
 ▫ vì vậy cần kiểm chứng = chương trình để : tránh mệt mỏi, giảm 
 sự bất cẩn  
 ▫ Tạo testing code 
 ▸ Viết 1 bộ kiểm chứng để kiểm tra toàn bộ chương trình mỗi khi có 
 sự thay đổi, sau khi biên dịch thành công 
 ▫ Cần biết cái gì được chờ đợi 
 ▸ Tạo đầu ra các ra, sao cho dễ dàng nhận biết làhay đúng sai 
 Kiểm chứng tự động 
 Automation 
▪ Tự động hóa kiểm chứng lùi 
 ▫ Tuần tự kiểm chứng so sánh các phiên bản mới với những 
 phiên bản cũ tương ứng. 
 ▫ Mục đích: đảm bảo việc sửa lỗi sẽ không làm ảnh hưởng 
 những phần khác trừ khi chúng ta muốn 
 ▫ 1 số hệ thống có công cụ trợ giúp kiểm chứng tự động : 
 ▸ Ngôn ngữ scripts : cho phép viết các đoạn script để test tuần tự 
 ▸ Unix : có các thao tác trên tệp tin như cmp và diff để so sanh dữ 
 liệu xuất, sort sắp xếp các phần tử, grep để kiểm chứng dữ liệu 
 xuất, wc, sum va freq để tổng kết dữ liệu xuất 
 ▫ Khi kiểm chứng lùi, cần đảm bảo phiên bản cũ là đúng, nếu 
 sai thì rất khó xác định và kết quả sẽ không chính xác 
 ▫ Cần phải kiểm tra chính việc kiểm chứng lùi 1 cách định kỳ 
 để đảm nó bảo vẫn hợp lệ 
 Kiểm chứng tự động 
 Automation 
▪ Dùng các công cụ test 
 - QuickTest professional 
 - TestMaker 
 - Rational Robot 
 - Jtest 
 - Nunit 
 - Selenium 
 - . 
 Kiểm chứng hướng lỗi 
 Bug-driven 
(4) Kiểm chứng hướng lỗi: Bug-driven testing 
 ▫ Tìm thấy 1 bug => Ngay lập tức tạo 1 test để bắt lỗi 
 ▫ Đơn giản hóa việc kiểm chứng lùi 
(5) Fault injection 
 ▫ Chủ động (tạm thời) cài các bugs!!! 
 ▫ Rồi quyết định nếu tìm thấy chúng 
 ▫ Kiểm chứng bản thân kiểm chứng!!! 
Các 
phương pháp 
kiểm thử 
 Các phương pháp 
 kiểm thử 
▪ Black-Box: Testing chỉ dựa trên việc phân tích các yêu cầu - 
requirements (unit/component specification, user 
documentation, v.v.). Còn được gọi là functional testing. 
▪ White-Box: Testing dựa trên việc phân tích các logic bên 
trong - internal logic (design, code, v.v.). (Nhưng kết quả 
mong đợi vẫn đến từ requirements.) Còn đc gọi là structural 
testing. 
 Kiểm thử 
 hộp trắng 
▪ Còn được gọi là clear box testing, glass box testing, 
transparent box testing, or structural testing, thường thiết kế 
các trường hợp kiểm thử dựa vào cấu trúc bên trong của 
phần mềm. 
▪ WBT đòi hỏi kĩ thuật lập trình am hiểu cấu trúc bên trong 
của phần mềm (các đường, luồng dữ liệu, chức năng, kết quả) 
▪ Phương thức: Chọn các đầu vào và xem các đầu ra 
 Kiểm thử 
 hộp trắng 
Đặc điểm 
▪ Phụ thuộc vào các cài đặt hiện tại của hệ thống và của phần 
mềm, nếu có sự thay đổi thì các bài test cũng cần thay đổi 
theo. 
▪ Được ứng dụng trong các kiểm tra ở cấp độ mô đun (điển 
hình), tích hợp (có khả năng) và hệ thống của quá trình test 
phần mềm. 
 Kiểm thử 
 hộp đen 
▪ Black-box testing sử dụng mô tả bên ngoài của phần mềm 
để kiểm thử, ,bao gồm các đặc tả (specifications), yêu cầu 
(requirements) và thiết kế (design). 
▪ Không có sự hiểu biết cấu trúc bên trong của phần mềm 
▪ Các dạng đầu vào có dạng hàm hoặc không, hợp lệ và không 
không hợp lệ và biết trước đầu hợp lệ và không không hợp lệ 
và biết trước đầu ra 
▪ Được sử dụng để kiểm thử phần mềm tại mức: mô đun, tích 
hợp, hàm, hệ thống và chấp nhận. 
 Kiểm thử 
 hộp đen 
▪ Ưu điểm của kiểm thử hộp đen là khả năng đơn giản hoá 
kiểm thử tại các mức độ được đánh giá là khó kiểm thử 
▪ Nhược điểm là khó đánh giá còn bộ giá trị nào chưa được 
kiểm thử hay không 
 Kiểm thử 
 hộp đen 
Các kỹ thuật chính của kiểm thử hộp đen 
▪ Decision Table testing 
▪ Pairwise testing 
▪ State transition tables 
▪ Tests of Customer Requirement 
▪ Equivalence partitioning 
▪ Boundary value analysis 
▪ Failure Test Cases 
 Kiểm thử 
 hộp xám 
▪ Là sự kết hợp của kiểm thử hộp đen và kiểm thử hộp trắng 
khi mà người kiểm thử biết được một phần cấu trúc bên 
trong của phần mềm 
 • Khác với kiểm thử hộp đen 
▪ Là dạng kiểm thử tốt và có sự kết hợp các kĩ thuật của cả 
kiểm thử hộp đen và các kĩ thuật của cả kiểm thử hộp đen và 
hộp trắng 
 Ai kiểm thử 
 cái gì? 
▪ Programmers 
 ▫ White-box testing 
 ▫ Ưu điểm: Người triển khai nắm rõ mọi luồng dữ liệu 
 ▫ Nhược: Bị ảnh hưởng bởi cách thức code đc thiết kê/viết 
▪ Quality Assurance (QA) engineers 
 ▫ Black-box testing 
 ▫ Pro: Không có khái niệm về implementation 
 ▫ Con: Không muốn test mọi logical paths 
▪ Customers 
 ▫ Field testing 
 ▫ Pros: Có các cách sử dụng chương trình bất ngờ;dễ gây lỗi 
 ▫ Cons: Không đủ trường hợp; khách hàng không thích tham gia 
 vào quá test trình ; 
 Các phương pháp 
 khác 
▪ Sanity testing 
▪ Smoke testing 
▪ Software testing 
▪ Stress testing 
▪ Test automation 
▪ Web Application Security Scanner 
▪ Fuzzing 
▪ Acceptance testing 
▪ Sanwich Testing 
 Các mức độ 
 kiểm thử 
▪ Unit: testing các mẫu công việc nhỏ nhất của lập trình viên để có 
thể lập kế hoạch và theo dõi hợp lý (vd : function, procedure, 
module, object class,) 
▪ Component: testing 1 tập hợp các units tạo thành 1 thành phần 
(vd: program, package, task, interacting object classes,) 
▪ Product: testing các thành phần tạo thành 1 sản phẩm 
(subsystem, application,) 
▪ System: testing toàn bộ hệ thống 
▪ Testing thường: 
 ▫ Bắt đầu = functional (black-box) tests, 
 ▫ Rồi thêm = structural (white-box) tests, và 
 ▫ Tiến hành từ unit level đến system level với 1 hoặc một vài bước tích 
 hợp 
 Kiểm thử 
 tất cả mọi thứ? 
Chi phí cho 'exhaustive' testing: 
 20 x 4 x 3 x 10 x 2 x 100 = 480,000 tests 
Nếu 1 giây cho 1 test, 8000 phút, 133 giờ, 17.7 ngày 
 (chưa kể nhầm lẫn hoặc test đi test lại) 
 secsnếu 10= 34 wks, 1 min = 4 yrs, 10 min = 40 yrs 
 Bao nhiêu testing 
 là đủ? 
▪ Không bao giờ đủ! 
▪ Khi bạn thực hiện những test mà bạn đã lên kế hoạch 
▪ Khi khách hàng / người sử dụng thấy thỏa mãn 
▪ Khi bạn đã chứng minh được / tin tưởng rằng hệ thống hoạt 
động đúng, chính xác 
▪ Phụ thuộc vào risks for your system 
▪ Càng ít thời gian, càng nhiều để thời gian để test luôn có giới 
hạn 
▪ Dùng RISK để xác định: 
 ▫ Cái gì phải test trước 
 ▫ Cái gì phải test nhiều 
 ▫ Mỗi phần tử cần test kỹ như thế nào? Tức là đâu là trọng tâm 
 ▫ Cái gì cầnkhông test (tại thời điểm này) 
 The Testing 
 Paradox 
▪ Mục đích của testing: để tìm ra lỗi 
▪ Tìm thấy lỗi làm hủy hoại sự tự tin 
=> Mục đích của testing: hủy hoại sự tự tin 
▪ Nhưng mục đích của testing: Xây dựng niềm tin, tự tin 
=> Cách tốt nhất để xây dựng niềm tin là: Cố gắng hủy hoại nó 
Thanks! 
 Any questions? 
 Email me at trungtt@soict.hust.edu.vn 
 Presentation template by SlidesCarnival 

File đính kèm:

  • pdfbai_giang_ky_thuat_lap_trinh_chuong_9_go_loi_va_kiem_thu.pdf