Bài giảng Kỹ thuật lập trình - Chương 4: Các kỹ thuật kiểm tra tính đúng đắn và tính an toàn của chương trình phần mềm - Vũ Thị Hương Giang

Kiểm tra cái gì để phát hiện lỗi ? • Kiểm tra mọi thao tác có thể gây lỗi khi viết CT – Nhập dữ liệu – Sử dụng dữ liệu • Ví dụ: – Kiểm tra mỗi lần mở một tệp tin hay cấp phát các ô nhớ. – Kiểm tra các phương thức người dùng nhập dữ liệu vào cho đến khi không còn nguy cơ gây ra dừng chương trình – Trong trường hợp tràn bộ nhớ (out of memory), nên in ra lỗi kết thúc chương trình (-1: error exit); – Trong trường hợp dữ liệu do người dùng đưa vào bị lỗi, tạo cơ hội cho người dùng nhập lại dữ liệu (lỗi tên file cũng có thể do người dùng nhập sai)Làm gì khi phát hiện lỗi ? • Cần có cách xử lý các lỗi mà ta chờ đợi sẽ xảy ra • Tùy theo tình huống cụ thể, ta có thể – Trả về 1 giá trị trung lập – Thay thế đoạn tiếp theo của dữ liệu hợp lệ – Trả về cùng giá trị như lần trước – Thay thế giá trị hợp lệ gần nhất – Ghi vết 1 cảnh báo vào tệp – Trả về 1 mã lỗi – Gọi 1 thủ tục hay đối tượng xử lý – Hiện thông báo lỗi – Tắt máy

pdf128 trang | Chia sẻ: thanhle95 | Lượt xem: 595 | Lượt tải: 1download
Bạn đang xem trước 20 trang tài liệu Bài giảng Kỹ thuật lập trình - Chương 4: Các kỹ thuật kiểm tra tính đúng đắn và tính an toàn của chương trình phần mềm - Vũ Thị Hương Giang, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
• Với mỗi bài toán, làm thế nào để: – Thiết kế giải thuật nhằm giải quyết bài toán đó – Cài đặt giải thuật bằng một chương trình máy tính - Hãy làm cho chương trình chạy đúng trước khi tăng tính hiệu quả của chương trình - Hãy tăng tính hiệu quả của chương trình và đồng thời thể hiện tốt phong cách lập trình của cá nhân CHƯƠNG IV. CÁC KỸ THUẬT KIỂM TRA TÍNH ĐÚNG ĐẮN VÀ TÍNH AN TOÀN CỦA CHƯƠNG TRÌNH PHẦN MỀM I. Bẫy lỗi (error handling) II. Lập trình phòng ngừa (defensive programming) III. Kiểm thử (Testing) IV. Gỡ rối (Debugging) Mở đầu • Lỗi: chương trình chạy không đúng như đã định • Chuỗi kiểm tra chương trình – Bẫy lỗi (error handling) – Lập trình phòng ngừa (defensive programming) – Kiểm thử (testing) – Gỡ rối (debugging) • Phân biệt: – Bẫy lỗi: Prevent errors – Lập trình phòng ngừa: Detect problems as early as possible – Kiểm thử: finished code – Gỡ rối: fixing defects uncovered by testing I. BẪY LỖI Nguyên tắc • Khi lỗi xảy ra cần – Định vị nguồn gây lỗi – Kiểm soát lỗi • Luôn có ý thức đề phòng các lỗi hay xảy ra trong chương trình, nhất là khi đọc file, dữ liệu do người dùng nhập vào và cấp phát bộ nhớ. • Áp dụng các biện pháp phòng ngừa ngay cả khi điều đó có thể dẫn tới việc dừng chương trình • In các lỗi bằng stderr stream. fprintf (stderr,"There is an error!\n"); Kiểm tra cái gì để phát hiện lỗi ? • Kiểm tra mọi thao tác có thể gây lỗi khi viết CT – Nhập dữ liệu – Sử dụng dữ liệu • Ví dụ: – Kiểm tra mỗi lần mở một tệp tin hay cấp phát các ô nhớ. – Kiểm tra các phương thức người dùng nhập dữ liệu vào cho đến khi không còn nguy cơ gây ra dừng chương trình – Trong trường hợp tràn bộ nhớ (out of memory), nên in ra lỗi kết thúc chương trình (-1: error exit); – Trong trường hợp dữ liệu do người dùng đưa vào bị lỗi, tạo cơ hội cho người dùng nhập lại dữ liệu (lỗi tên file cũng có thể do người dùng nhập sai) Làm gì khi phát hiện lỗi ? • Cần có cách xử lý các lỗi mà ta chờ đợi sẽ xảy ra • Tùy theo tình huống cụ thể, ta có thể – Trả về 1 giá trị trung lập – Thay thế đoạn tiếp theo của dữ liệu hợp lệ – Trả về cùng giá trị như lần trước – Thay thế giá trị hợp lệ gần nhất – Ghi vết 1 cảnh báo vào tệp – Trả về 1 mã lỗi – Gọi 1 thủ tục hay đối tượng xử lý – Hiện thông báo lỗi – Tắt máy Một số lỗi nhập dữ liệu phổ biến • Dữ liệu nhập vào quá lớn (ví dụ, vượt quá kích thước kích thước lưu trữ cho phép của mảng hay của biến) • Dữ liệu nhập vào sai kiểu, giá trị quá nhỏ hoặc giá trị âm • Lỗi chia cho số 0 (divide by zero) Dùng hàm bao gói (Wrappered function) • Hàm bao gói = gọi hàm gốc + bẫy lỗi • Tại mọi thời điểm cần kiểm tra lỗi của hàm gốc, dùng hàm bao gói thay vì dùng hàm gốc • Ví dụ: • Nếu phải viết đoạn mã kiểm tra lỗi mỗi lần sử dụng malloc thì rất nhàm chán. – Kiểm tra – Thông báo lỗi kiểu như “out of memory” – Thoát. • Tự viết hàm safe_malloc và sử dụng hàm này thay vì dùng malloc có sẵn – Malloc – Kiểm tra – Thông báo lỗi kiểu như “out of memory” – Thoát. safe_malloc #include #include void *safe_malloc (size_t); /* Bẫy lỗi khi dùng hàm malloc */ void *safe_malloc (size_t size) /* Cấp phát bộ nhớ hoặc báo lỗi và thoát */ { void *ptr; ptr= malloc(size); if (ptr == NULL) { fprintf (stderr, “Không đủ bộ nhớ để thực hiện dòng lệnh :%d file :%s\n", __LINE__, __FILE__); exit(-1); } return ptr; } Tạo giao diện rõ ràng • Các LTV giỏi luôn tìm cách làm cho mã nguồn của họ trở nên hữu dụng với những LTV khác. • Cách tốt nhất để làm việc này là viết các hàm dễ hiểu, có khả năng tái sử dụng • Các hàm tốt không cần đến quá nhiều tham số truyền vào • Để viết tốt các hàm, cần tư duy theo hướng: – Cần truyền cái gì vào để thực hiện hàm ? – Có thể lấy được cái gì ra sau khi thực hiện hàm • Nếu LTV có khả năng viết được một giao diện rõ ràng thì các hàm tự bản thân nó trở nên hiệu quả: – Các hàm được cung cấp – Cách thức truy nhập chức năng muốn cung cấp Ví dụ: giao diện thể hiện được cấu trúc của chương trình pay.h Header file - enums, structs, prototypes pay.cpp #include "pay.h" int main() Lấy dữ liệu vào từ người dùng Gọi các chương trình con hợp lý fileio.cpp #include "pay.h" Đọc các records khi được gọi Ghi vào các records Tạo bản sao update.cpp #include "pay.h" Tạo file mới cho mỗi NV mới Ghi vào file đang có cho NV đã có tên printout.cpp #include "pay.h" In bảng lương của tất cả các nhân viên In các bảng lương của từng nhân viên để đối chiếu Đơn giản hóa các hàm bằng cách cấu trúc hóa chương trình • Đôi khi cần truyền rất nhiều tham số vào một hàm void write_record (FILE *fptr, char name[], int wage, int hire_date, int increment_date, int pay_scale, char address[]) /* Hàm ghi thông tin liên quan đến 1 NV */ { } void write_record (FILE *fptr, EMPLOYEE this_employee) /* Hàm ghi thông tin liên quan đến 1 NV */ { } Sẽ tốt hơn nếu xây dựng 1 struct và thao tác trên struct đó Các hàm phải nhất quán (consistent) • Nếu cần tạo ra loạt các hàm tương tự nhau, thì nên tổ chức mã nguồn của các hàm đó sao cho logic hay quy trình nghiệp vụ của các hàm đó là như nhau int write_record (char fname[],EMPLOYEE employee) /* Trả về -1 nếu ghi bị lỗi, trả về 0 nếu ghi thành công*/ { } int add_record (EMPLOYEE employee, char fname[]) /* Trả về 0 nếu lỗi, trả về 1 nếu thành công*/ { } Hàm thứ 2 khác hẳn hàm thứ nhất Khi tái sử dụng, khả năng gây lỗi là rất cao Không tùy tiện thay đổi cách thức hoạt động của hàm • Chỉ thay đổi tham số của hàm nếu không còn lựa chọn nào khác. • Không thay đổi mục tiêu, nhiệm vụ của hàm: ví dụ: nếu mục tiêu ban đầu không phải là cập nhật mảng, thì trong hàm đừng thực hiện việc cập nhật mảng FILE *fptr; char fname[]= "file.txt"; fptr= fopen (fname, "r"); if (fptr == NULL) { printf ("Can't open %s\n",fname); return -1; } Nếu fopen làm thay đổi không báo trước giá trị lưu trong fname, điều gì sẽ xảy ra? Buffer Overflow & vấn đề truy nhập trái phép • Hacker có thể khai thác các lỗi (bug) của hệ điều hành (Windows/Unix/Mac OS) hay phần mềm để truy nhập trái phép vào máy tính • Một trong những lỗi được khai thác rất nhiều là "buffer overflows". • Có nhiều cách gây ra buffer overflows – Sử dụng cẩu thả "strcpy" (sao chép 1 xâu lớn hơn vào 1 biến có kích thước nhỏ hơn). – Dùng lệnh gets (thư viện stdio) – Quên không kiểm tra độ lớn của xâu vào – .. • Kiểm tra dữ liệu vào từ người dùng là một trong những cách hạn chế truy nhập trái phép Buffer Overflow • Cái gì xảy ra nếu xâu lưu trong str2 dài hơn xâu lưu trong str1? void strcpy(char str1[], char str2[]) /* Copy xâu từ str2 vào str1 */ { int i= 0; while ((str1[i]= str2[i]) != '\0') i++; } Another complex line which assigns and compares. Buffer overflows • Cái gì xảy ra nếu người dùng nhập vào 10 ký tự ? 100 ký tự ? #include int main(void) { char name [25]; printf("Enter your name: "); fflush(stdout); if (gets(name) != NULL) printf("Hello and Goodbye %s\n", name); return 0; } safe-gets • Dùng fgets() thay thế cho gets(). – fgets() không tự loại bỏ \n (new line) ở cuối xâu như gets() #include #include char *safeget(char *buffer, int count) { char *result = buffer, *np; if ((buffer == NULL) || (count < 1)) result = NULL; else if (count == 1) *result = '\0'; else if ((result = fgets(buffer, count, stdin)) != NULL) if (np = strchr(buffer, '\n')) *np = '\0'; return result; } Khai thác lỗi overflow như thế nào ? Bộ nhớ máy tính Mảng có khả năng bị tràn Code khác Chương trình đang chạy Ghi vào mảng (gửi dữ liệu có chứa chương trình gây hại vào CT đang chạy ) Lots of "no operations" Chương trình gây hại Chương trình đang chạy thử tiếp tục chạy trên phần bộ nhớ dành cho nó, nhưng mà phần bộ nhớ này đã bị chương trình gây hại chiếm mất khi thực hiện phép ghi vào mảng II. LẬP TRÌNH PHÒNG NGỪA 1. Khái niệm • Xuất phát từ khái niệm defensive driving. – Khi lái xe bạn luôn phải tâm niệm rằng bạn không bao giờ biết chắc được người lái xe khác sẽ làm gì. Bằng cách đó, bạn có thể chắc chắn rằng khi họ làm điều gì nguy hiểm, thì bạn sẽ không bị ảnh hưởng (tai nạn). – Bạn có trách nhiệm bảo vệ bản thân, ngay cả khi người khác có lỗi 1. Khái niệm • Ý tưởng chính: nếu chương trình (CTC) nhận dữ liệu vào bị lỗi thì nó vẫn chạy thông, ngay cả khi CT khác cũng nhận dữ liệu đầu vào đó đã bị lỗi. • Lập trình phòng ngừa là cách tự bảo vệ CT của mình khỏi – các ảnh hưởng tiêu cực của dữ liệu không hợp lệ – các rủi ro đến từ các sự kiện tưởng như "không bao giờ" xảy ra – sai lầm của các lập trình viên khác 2. Các lỗi có thể phòng ngừa • Lỗi liên quan đến phần cứng – Đảm bảo các lỗi như buffer overflows hay divide by zero được kiểm soát • Lỗi liên quan đến chương trình – Đảm bảo giá trị gán cho các biến luôn nằm trong vùng kiểm soát – Do not trust anything; verify everything • Lỗi liên quan đến người dùng – Đừng cho rằng người dùng luôn thực hiện đúng các thao tác theo chỉ dẫn, hãy kiểm tra mọi thao tác của họ • Lỗi liên quan đến các kỹ thuật phòng ngừa! – Mã nguồn cài đặt các kỹ thuật phòng ngừa cũng có khả năng gây lỗi, kiểm tra kỹ phần này 3. Các giai đoạn lập trình phòng ngừa • Lập kế hoạch thực hiện công việc: – Dành thời gian để kiểm tra và gỡ rối chương trình cẩn thận : hoàn thành chương trình trước ít nhất 3 ngày so với hạn nộp • Thiết kế chương trình: – Thiết kế giải thuật trước khi viết bằng ngôn ngữ lập trình cụ thể • Giữ vững cấu trúc chương trình: – Viết và kiểm thử từng phần chương trình: phần chương trình nào dùng để làm gì – Viết và kiểm thử mối liên kết giữa các phần trong chương trình: quy trình nghiệp vụ như thế nào – Phòng ngừa bằng các điều kiện trước và sau khi gọi mỗi phần chương trình: điều gì phải đúng trước khi gọi chương trình, điều gì xảy ra sau khi chương trình thực hiện xong – Dùng chú thích để miêu tả cấu trúc chương trình khi viết chương trình Kiểm tra cái gì, khi nào ? • Testing: chỉ ra các vấn đề làm chương trình không chạy • Kiểm tra theo cấu trúc của chương trình: Kiểm tra việc thực hiện các nhiệm vụ đặt ra cho từng phần chương trình – Ví dụ: điều gì xảy ra với chương trình căn lề văn bản, nếu hàm ReadWord() bị lỗi ? • Nếu chương trình không có tham số đầu vào, mà chỉ thực thi nhiệm vụ và sinh ra kết quả thì không cần kiểm tra nhiều. Hầu hết chương trình đều không như vậy – Ví dụ: điều gì xảy ra với chương trình căn lề văn bản, nếu • Không nhập đầu vào ? • Đầu vào không phải là xâu/file chứa các từ hay chữ cái đúng quy định ? a. Kiểm tra tham số đầu vào • Một phần chương trình chạy thông 1 lần không có nghĩa là lần tiếp theo nó sẽ chạy thông. • Chương trình trả ra kết quả đúng với đầu vào 'n' không có nghĩa là nó sẽ trả ra kết quả đúng với đầu vào ‘m’ ‘n’. • Vậy chương trình có thực sự chạy thông không ? – Với bất cứ đầu vào nào chương trình cũng phải chạy thông, không bị “crash”. Nếu có lỗi thì chương trình phải dừng và thông báo lỗi – Bạn có thể biết chương trình có chạy thông hay không khi kiểm tra chương trình bằng các tham số đầu vào sai Tham số đầu vào sai • Trong thực tiễn: “Garbage in, garbage out.” – GIGO • Trong lập trình, “rác vào – rác ra” là dấu hiệu của những CT tồi, không an toàn • Với 1 CT tốt thì: – “rác vào, không có gì ra”, – “rác vào, có thông báo lỗi” – “không cho phép rác vào”. Phòng ngừa lỗi tham số vào • Check the values of all routine input parameters – Kiểm tra giá trị của tất cả các tham số truyền vào các hàm cùng cần như kiểm tra dữ liệu nhập từ nguồn ngoài khác • Decide how to handle bad inputs – Khi phát hiện 1 tham số hay 1 dữ liệu không hợp lệ, bạn cần làm gì với nó? Tùy thuộc tình huống, bạn có thể chọn 1 trong các phương án phù hợp Phòng ngừa lỗi tham số vào • Kiểm tra giá trị của mọi dữ liệu từ nguồn bên ngoài – Khi nhận dữ liệu từ file, bàn phím, mạng, hoặc từ các nguồn ngoài khác, hãy kiểm tra để đảm bảo rằng dữ liệu nằm trong giới hạn cho phép. – Hãy đảm bảo rằng giá trị số nằm trong dung sai và xâu phải đủ ngẵn để xử lý • Nếu một chuỗi cần trong một phạm vi giới hạn của các giá trị (như một ID giao dịch tài chính), hãy chắc chắn rằng chuỗi đầu vào là hợp lệ cho mục đích của nó; nếu không từ chối. – Với ứng dụng bảo mật, hãy đặc biệt lưu ý đến những dữ liệu có thể tấn công hệ thống: Cố làm tràn bộ nhớ, injected SQL commands, injected html hay XML code, tràn số Ví dụ • Đoạn mã nguồn sau tìm giá trị trung bình của n giá trị kiểu doubles. • Chương trình bị lỗi khi nào ? double avg (double a[], int n) /* a là mảng gồm n số kiểu doubles */ { int i; double sum= 0; for (i= 0; i < n; i++) { sum+= a[i]; } return sum/n; } Phòng ngừa lỗi tham số vào • Trong một số trường hợp, phải viết thêm các đoạn mã nguồn để lọc giá trị đầu vào trước khi tính toán void class_of_degree (char degree[], double percent) /* Xếp hạng sinh viên dựa vào tổng điểm tính theo % */ { if (percent 100) strcpy(degree,"Error in mark"); else if (percent >= 70) strcpy(degree,"First"); else if (percent >= 60) strcpy(degree,"Two-one"); . . } Kiểm tra điều kiện biên • Điều gì xảy ra nếu giá trị đầu vào quá lớn hay quá nhỏ ? • Hãy chắc chắn là chương trình của bạn có thể đối phó với các tham số đầu vào kiểu này • Luôn kiểm tra trường hợp “divide by zero error” Ví dụ • Hàm sau đây mô phỏng hàm strlen trong thư viện chuẩn của C. Viết thêm các dòng lệnh phòng ngừa lỗi int my_strlen (char *string) /* Khi tính độ dài xâu, hàm này sai ở đâu */ { int len= 1; while (string[len] != '\0') len++; return len; } c. Tràn số (overflow of numbers) • Arian 5: • Chi phí phát triển: 7 tỷ USD • Phụ kiện hàng hóa đi kèm : 370 triệu USD • Thực hiện chuyển đổi 64 bit dấu phẩy động sang 16 bit số nguyên: Việc chuyển đổi không thành công do tràn số • 04/06/1996: 37 giây sau khi phóng, nổ ở độ cao 3700m Tràn số (overflows of numbers) • Nếu cần tính toán với các số lớn, hãy chắc chắn là bạn biết giá trị lớn nhất mà biến bạn dùng có khả năng lưu trữ • Ví dụ: – Với phần lớn trình dịch C, 1 unsigned char có giá trị từ 0 đến 255. – Kích thước tối đa của 1 biến kiểu int có thể thay đổi 5. Kiểm soát lỗi có thể xảy ra • Error handling: xử lý các lỗi mà ta dự kiến sẽ xảy ra • Tùy theo tình huống cụ thể, ta có thể trả về: – 1 giá trị trung lập – thay thế đoạn tiếp theo của dữ liệu hợp lệ – trả về cùng giá trị như lần trước – thay thế giá trị hợp lệ gần nhất – ghi vết 1 cảnh báo vào tệp – trả về 1 mã lỗi – gọi 1 thủ tục hay đối tượng xử lý – hiện 1 thông báo hay tắt máy Chắc chắn hay chính xác ? • Chắc chắn: CT luôn chạy thông, kể cả khi có lỗi • Chính xác: CT không bao giờ gặp lại lỗi • Ví dụ: Lỗi hiện thị trong các trình xử lý văn bản: khi đang thay đổi nội dung văn bản, thỉnh thoảng một phần của một dòng văn bản ở phía dưới màn hình bị hiện thị sai. Khi đó người dùng phải làm gì? – Tắt CT – Nhấn PgUp hoặc PgDn, màn hình sẽ làm mới • Ưu tiên tính chắc chắn thay vì tính chính xác: – Bất cứ kết quả nào đó bao giờ cũng thường là tốt hơn so với Shutdown. Khi nào phải loại bỏ hết lỗi ? • Đôi khi, để loại bỏ 1 lỗi nhỏ, lại rất tốn kém – Nếu lỗi đó chắc chắn không ảnh hưởng đến mục đích cơ bản của ứng dụng, không làm chương trình bị treo, hoặc làm sai lệch kết quả chính, người ta có thể bỏ qua, mà không cố sửa để có thể gặp phải các nguy cơ khác. • Phần mềm “chịu lỗi”?: Phần mềm sống chung với lỗi, để đảm bảo tính liên tục, ổn định 5. Xử lý ngoại lệ • Một chương trình biên dịch thành công, vẫn có thể gây ra những lỗi khi chạy, đó chính là các ngoại lệ (exception). – Điều kiện chủ quan: Do lập trình sai – Điều kiện khách quan: Do nhập sai dữ liệu, do trạng thái của hệ thống (tràn bộ nhớ). • Ngoại lệ phá vỡ luồng bình thường của CT • Khi xảy ra một ngoại lệ, nếu không xử lý thì chương trình kết thúc ngay và trả lại quyền điều khiển cho hệ điều hành. EXIT NO HANDLER EXISTS float number1, number2; //nhập vào 2 số float division=number1/number2; Báo hiệu tình tình huống bất thường • Bắt các tình huống bất thường và phục hồi chúng về trạng thái trước đó • Giúp chương trình đáng tin cậy hơn, tránh kết thúc bất thường • Tách biệt khối lệnh có thể gây ngoại lệ và khối lệnh xử lý ngoại lệ IF B IS ZERO GO TO ERROR C = A/B PRINT C GO TO EXIT ERROR: DISPLAY “DIVISION BY ZERO” EXIT: END Khối xử lý lỗi Báo hiệu điều kiện lỗi • Thông báo cho các bộ phận khác của chương trình về lỗi không nên bỏ qua • Chỉ dùng ngoại lệ cho những điều kiện thực sự ngoại lệ – Exception được dùng trong những tình huống giống assertion cho các sự kiện không thường xuyên, nhưng có thể không bao giờ xảy ra – Exception có thể bị lạm dụng và phá vỡ các cấu trúc, điều này dễ gây ra lỗi, vì làm sai lệch luồng điều khiển Ví dụ chia cho 0 public class ChiaCho0Demo { public static void main(String args[]){ try { int num = calculate(9,0); System.out.println(num); } catch(Exception e) { System.err.println("Co loi xay ra: " + e.toString()); } } static int calculate(int no, int no1){ int num = no / no1; return num; } } Phục hồi tài nguyên khi có ngoại lệ • Thường thì không phục hồi tài nguyên • Nhưng sẽ hữu ích khi thực hiện các công việc nhằm đảm bảo cho thông tin ở trạng thái rõ ràng và vô hại nhất có thể • Nếu các biến vẫn còn được truy xuất thì chúng nên được gán các giá trị hợp lý • Trường hợp thực thi việc cập nhật dữ liệu, nhất là trong 1 phiên – transaction – liên quan tới nhiều bảng chính, phụ, thì việc khôi phục khi có ngoại lệ là vô cùng cần thiết (rollback) Kết luận Keeping It Simple (KISS): complex code is more likely to contain errors. Controlling complexity is the essence of computer programming. -- Brian Wilson Kernighan Kiểm tra mã nguồn trong khi viết chương trình • 1 LTV giỏi không ngồi viết 1 lúc 10000 dòng lệnh rồi chạy thử chương trình • Mọi việc sẽ dễ dàng hơn khi vừa viết chương trình vừa kiểm tra • Xác định cấu trúc của chương trình, viết từng phần nhỏ của chương trình và kiểm tra luôn phần vừa viết. • Hoàn thiện từng phần chương trình, kiểm tra lại sau khi hoàn thiện. • Mẹo: – Nếu có sửa đổi mã nguồn thì dịch từng đoạn một, khoảng chục dòng – Sử dụng nhiều cửa sổ dịch III. KIỂM THỬ a. Mục đích • Khó có thể khẳng định 1 CT lớn có làm việc chuẩn hay không • Khi XD 1 CT lớn, 1 LTV chuyên nghiệp sẽ dành thời gian cho việc viết test code không ít hơn thời gian dành cho viết CT • LTV chuyên nghiệp là người có khả năng, kiến thức rộng về các kỹ thuật và chiến lược testing b. Khái niệm • Beizer: Testing = chứng minh tính đúng đắn giữa 1 phần tử và các đặc tả của nó. • Myers: Testing = quá trình thực hiện 1 CT với mục đích tìm ra những lỗi. • IEEE: Testing = quá trình kiểm tra hay đánh giá 1 hệ thống hay 1 thành phần hệ thống một cách thủ công hay tự động để kiểm thử rằng nó thỏa mãn những yêu cầu đặc thù hoặc để xác định sự khác biệt giữa kết quả mong đợi và kết quả thực tế c. Testing vs. debugging • Testing & debugging đi cùng với nhau như 1 cặp: – Testing tìm errors; debugging định vị và sửa chúng. – Ta có mô hình “testing/debugging cycle”: Ta test, rồi debug, rồi lặp lại. – Bất kỳ 1 debugging nào nên được tiếp theo là 1 sự áp dụng lại của hàng loạt các tests liên quan, đặc biệt là các bài tests hồi quy. Điều này giúp tránh nảy sinh các lỗi mới khi debugging. –
Tài liệu liên quan