Luận văn Thạc sĩ: Xây dựng công cụ sinh đột biến cho chương trình Lustre

Luận văn trình bày công cụ sinh đột biến tự động cho chương trình Lustre, giúp tối ưu hóa kiểm thử đột biến và nâng cao khả năng phát hiện lỗi hệ thống.

Trường đại học

Đại học Đà Nẵng

Chuyên ngành

Khoa học máy tính

Người đăng

Ẩn danh

Thể loại

Luận văn thạc sĩ

2017

78
1
0

Phí lưu trữ

30 Point

Tóm tắt

I. Hướng dẫn kiểm thử đột biến Lustre cho hệ thống an toàn

Trong lĩnh vực phát triển phần mềm cho các hệ thống an toàn-critical (safety-critical systems), việc đảm bảo chất lượng và độ tin cậy là yêu cầu bắt buộc. Ngôn ngữ lập trình Lustre, một ngôn ngữ đồng bộ luồng dữ liệu, đã trở thành một công cụ quan trọng để thiết kế các hệ thống phản ứng phức tạp như hệ thống điều khiển máy bay, lò phản ứng hạt nhân, hay hệ thống phanh ABS trên ô tô. Với các hệ thống này, một sai sót nhỏ trong phần mềm có thể gây ra hậu quả thảm khốc, do đó, quy trình xác minh và thẩm định (V&V - verification and validation) phải được thực hiện một cách nghiêm ngặt. Kiểm thử đột biến (mutation testing) nổi lên như một kỹ thuật kiểm thử hộp trắng mạnh mẽ, có khả năng đánh giá sâu sắc chất lượng của một bộ dữ liệu thử. Kỹ thuật này không chỉ tìm lỗi mà còn đo lường khả năng phát hiện lỗi của các ca kiểm thử. Nguyên lý cơ bản của kiểm thử đột biến dựa trên hai giả thuyết chính: "lập trình viên giỏi" (competent programmer hypothesis) và "hiệu ứng liên kết" (coupling effect hypothesis). Giả thuyết đầu tiên cho rằng các lập trình viên thường chỉ mắc phải những lỗi nhỏ, đơn giản. Giả thuyết thứ hai khẳng định rằng một bộ kiểm thử có khả năng phát hiện các lỗi đơn giản cũng sẽ có khả năng cao phát hiện các lỗi phức tạp hơn. Quy trình phân tích đột biến (mutation analysis) bắt đầu bằng việc tạo ra các phiên bản lỗi của chương trình gốc, gọi là các "đột biến" (mutants), bằng cách áp dụng các thay đổi cú pháp nhỏ thông qua các toán tử đột biến (mutation operators). Mục tiêu sau đó là tạo bộ kiểm thử (test suite generation) có khả năng "diệt" (kill) càng nhiều đột biến càng tốt, tức là làm cho kết quả đầu ra của đột biến khác với chương trình gốc.

1.1. Vai trò của ngôn ngữ lập trình Lustre trong hệ thống phản ứng

Lustre là một ngôn ngữ khai báo, được thiết kế đặc biệt cho các chương trình đồng bộ (synchronous programs). Đặc tính này làm cho nó cực kỳ phù hợp để mô hình hóa và phát triển các hệ thống phản ứng (reactive systems) - những hệ thống liên tục tương tác với môi trường và phải đáp ứng các ràng buộc thời gian thực nghiêm ngặt. Một chương trình Lustre được cấu thành từ các "node", mỗi node định nghĩa các mối quan hệ giữa luồng dữ liệu đầu vào và đầu ra. Khác với các ngôn ngữ lập trình mệnh lệnh, thứ tự các câu lệnh trong một node Lustre không quan trọng. Điều này cho phép thực hiện các phương pháp xác minh hình thức (formal verification) một cách hiệu quả, giúp chứng minh tính đúng đắn của chương trình. Các hệ thống được xây dựng bằng Lustre, chẳng hạn như các mô hình SCADE, yêu cầu mức độ tin cậy cao nhất, do đó, hoạt động kiểm thử đóng một vai trò không thể thiếu trong chu trình phát triển.

1.2. Nguyên lý cơ bản của phương pháp phân tích đột biến

Phân tích đột biến hoạt động bằng cách chèn các lỗi giả định vào mã nguồn. Mỗi lỗi được chèn thông qua một toán tử đột biến, tạo ra một phiên bản chương trình mới chỉ khác chương trình gốc ở một điểm duy nhất. Ví dụ, một toán tử có thể thay thế phép cộng (+) bằng phép trừ (-) hoặc thay đổi một toán tử quan hệ (>) thành (<=). Sau khi tạo ra một tập hợp các đột biến, chúng được thực thi với cùng một bộ dữ liệu thử như chương trình gốc. Nếu một ca kiểm thử tạo ra kết quả khác biệt giữa chương trình gốc và một đột biến, đột biến đó được coi là "bị diệt". Chất lượng của bộ dữ liệu thử được đo bằng điểm đột biến (mutation score) – tỷ lệ phần trăm các đột biến không tương đương đã bị diệt. Một điểm đột biến cao cho thấy bộ kiểm thử có năng lực phát hiện lỗi tốt.

II. Thách thức sinh đột biến Lustre Chi phí và độ phức tạp

Mặc dù kiểm thử đột biến cho Lustre là một phương pháp hiệu quả, việc áp dụng nó trong thực tế, đặc biệt với các dự án quy mô công nghiệp, gặp phải nhiều rào cản đáng kể. Thách thức lớn nhất đến từ chi phí tính toán và nguồn lực. Việc sinh đột biến bằng phương pháp thủ công là một công việc tốn nhiều thời gian, công sức và dễ xảy ra sai sót. Đối với một chương trình Lustre dù có quy mô vừa phải, số lượng đột biến có thể được tạo ra là rất lớn. Luận văn của Tạ Thanh Hải chỉ ra rằng, "số lượng các đột biến tạo ra xấp xỉ bằng tích của số các tham chiếu dữ liệu và số các đối tượng dữ liệu". Điều này có nghĩa là khi độ phức tạp của chương trình tăng lên, số lượng đột biến sẽ tăng theo cấp số nhân, khiến cho việc quản lý, biên dịch và thực thi từng đột biến trở nên không khả thi. Hơn nữa, mỗi đột biến cần được thực thi với ít nhất một ca kiểm thử, làm tăng gánh nặng về thời gian xử lý. Vấn đề thứ hai là sự tồn tại của các đột biến tương đương (equivalent mutants). Đây là những đột biến, mặc dù có cú pháp khác với chương trình gốc, nhưng lại tạo ra kết quả đầu ra giống hệt với mọi đầu vào khả dĩ. Việc xác định các đột biến này là một bài toán khó, thường đòi hỏi sự can thiệp thủ công của chuyên gia. Nếu không loại bỏ các đột biến tương đương, điểm đột biến sẽ không bao giờ đạt 100%, gây ra sự mơ hồ trong việc đánh giá chất lượng thực sự của bộ kiểm thử. Những khó khăn này đòi hỏi một giải pháp tự động hóa để giảm bớt gánh nặng và làm cho mutation testing for Lustre trở nên thực tế hơn.

2.1. Chi phí tính toán trong việc tạo và thực thi đột biến thủ công

Chi phí trong kiểm thử đột biến tập trung vào ba giai đoạn chính: sinh đột biến, biên dịch đột biến, và thực thi các ca kiểm thử trên từng đột biến. Khi thực hiện thủ công, mỗi giai đoạn đều tiêu tốn tài nguyên đáng kể. Người kiểm thử phải rà soát mã nguồn, xác định các vị trí có thể áp dụng toán tử đột biến, sau đó tạo ra từng tệp mã nguồn mới cho mỗi đột biến. Tiếp theo, mỗi tệp này phải được biên dịch riêng lẻ. Cuối cùng, toàn bộ bộ kiểm thử phải được chạy trên hàng trăm, thậm chí hàng nghìn phiên bản đột biến. Quy trình này không chỉ làm chậm tiến độ dự án mà còn tiềm ẩn nguy cơ lỗi do con người. Việc tự động hóa quá trình này là yêu cầu cấp bách để có thể áp dụng kiểm thử đột biến Lustre ở quy mô lớn.

2.2. Vấn đề nan giải về đột biến tương đương trong phân tích đột biến

Một đột biến được gọi là tương đương nếu nó luôn tạo ra kết quả giống hệt chương trình gốc, bất kể dữ liệu đầu vào. Ví dụ, câu lệnh x = y + z trong một ngữ cảnh if (y==2 && z==2) có thể bị đột biến thành x = y * z. Trong trường hợp này, cả hai phiên bản đều cho ra x = 4, khiến chúng trở nên tương đương và không thể bị "diệt". Việc phát hiện tự động các đột biến này là một vấn đề không thể giải quyết triệt để (undecidable problem). Trong thực tế, các kiểm thử viên thường phải phân tích thủ công từng đột biến "sống sót" để xác định xem chúng có tương đương hay không. Công việc này cực kỳ tốn thời gian và làm giảm hiệu quả của toàn bộ quy trình phân tích đột biến.

III. Phương pháp xây dựng công cụ sinh đột biến Lustre tự động

Để giải quyết các thách thức về chi phí và độ phức tạp, luận văn của Tạ Thanh Hải đã đề xuất và xây dựng một công cụ sinh đột biến tự động cho chương trình Lustre, được đặt tên là MuLustre. Phương pháp tiếp cận của công cụ này tập trung vào việc tự động hóa hoàn toàn quy trình từ phân tích mã nguồn đến tạo ra các tệp đột biến, giúp loại bỏ các thao tác thủ công tốn kém. Nền tảng của công cụ là một bộ phân tích cú pháp mạnh mẽ, có khả năng đọc và hiểu cấu trúc của một chương trình Lustre. Quy trình hoạt động của MuLustre bắt đầu bằng việc tiếp nhận một chương trình Lustre gốc làm đầu vào. Chương trình này sau đó được đưa qua một bộ phân tích cú pháp (Lustre Parser). Bộ phân tích này, được xây dựng dựa trên các công cụ Lex và Yacc, có nhiệm vụ tách mã nguồn thành các thành phần cú pháp cơ bản (tokens) và xây dựng một cây cấu trúc trừu tượng (Abstract Syntax Tree - AST) đại diện cho chương trình. Cây AST này là một dạng biểu diễn trung gian, lưu trữ tất cả thông tin về biến, hằng số, toán tử và cấu trúc lệnh của chương trình. Một khi đã có cây AST, công cụ sẽ duyệt qua cây này và xác định tất cả các vị trí mà các toán tử đột biến cho Lustre có thể được áp dụng. Dựa trên bộ toán tử đã được định nghĩa, công cụ sẽ tự động thực hiện các thay đổi trên cây AST để tạo ra các phiên bản đột biến. Cuối cùng, từ mỗi cây AST đã bị biến đổi, công cụ sẽ sinh ra một tệp mã nguồn Lustre hoàn chỉnh, tương ứng với một đột biến.

3.1. Kiến trúc hệ thống và quy trình hoạt động của công cụ MuLustre

Kiến trúc của MuLustre được thiết kế theo mô-đun để đảm bảo tính linh hoạt và khả năng mở rộng. Thành phần cốt lõi là Bộ phân tích cú pháp (Lustre Parser). Thành phần này chịu trách nhiệm biến đổi mã nguồn văn bản thành một cấu trúc dữ liệu mà máy tính có thể xử lý. Sau khi phân tích, một dạng trung gian (cây AST) được tạo ra. Tiếp theo, mô-đun Sinh đột biến (Mutant Generator) sẽ hoạt động. Mô-đun này chứa logic để áp dụng các mutation operators for Lustre lên cây AST. Mỗi toán tử đột biến được áp dụng sẽ tạo ra một bản sao của cây AST với một thay đổi nhỏ. Quy trình này được lặp lại cho đến khi tất cả các toán tử và tất cả các vị trí hợp lệ đều đã được xử lý. Kết quả là một tập hợp lớn các chương trình đột biến, sẵn sàng cho giai đoạn thực thi và đánh giá.

3.2. Vai trò của bộ phân tích cú pháp Lustre Parser Lex Yacc

Để tự động hóa việc sinh đột biến, bước đầu tiên và quan trọng nhất là phải "hiểu" được mã nguồn. MuLustre sử dụng hai công cụ kinh điển trong xây dựng trình biên dịch là Lex và Yacc. Lex (Lexical Analyzer Generator) chịu trách nhiệm phân tích từ vựng, tức là đọc chuỗi ký tự đầu vào và nhóm chúng thành các đơn vị có ý nghĩa gọi là token (ví dụ: từ khóa node, let, toán tử +, tên biến). Yacc (Yet Another Compiler-Compiler) sau đó nhận các token này và thực hiện phân tích cú pháp. Dựa trên một bộ quy tắc văn phạm được định nghĩa trước cho ngôn ngữ lập trình Lustre, Yacc xây dựng cây phân tích cú pháp, đảm bảo rằng chương trình tuân thủ đúng cú pháp của ngôn ngữ. Việc sử dụng Lex và Yacc giúp tự động hóa quá trình phân tích một cách hiệu quả và chính xác.

IV. TOP các toán tử đột biến cho Lustre được công cụ áp dụng

Hiệu quả của một hệ thống kiểm thử đột biến phụ thuộc rất lớn vào chất lượng và sự đa dạng của bộ toán tử đột biến được sử dụng. Các toán tử này được thiết kế để mô phỏng các lỗi phổ biến mà lập trình viên thường mắc phải. Công cụ MuLustre được xây dựng dựa trên một bộ 7 toán tử đột biến cho Lustre (mutation operators for Lustre) đã được định nghĩa và phân loại cẩn thận, bao trùm các lỗi về biến, hằng số và các loại biểu thức khác nhau. Các toán tử này đảm bảo rằng các đột biến được tạo ra có ý nghĩa và đại diện cho các sai sót tiềm tàng trong logic của chương trình. Việc lựa chọn và áp dụng các toán tử này một cách có hệ thống là chìa khóa để tạo bộ kiểm thử chất lượng, giúp cải thiện độ tin cậy của các hệ thống an toàn-critical. Mỗi toán tử nhắm vào một loại lỗi cụ thể, từ việc thay thế đơn giản một biến cho đến thay đổi logic của các biểu thức quan hệ hay số học. Ví dụ, toán tử thay thế toán tử số học (ARO) có thể thay đổi một phép cộng thành phép nhân, mô phỏng lỗi tính toán. Tương tự, toán tử thay thế toán tử quan hệ (RRO) có thể thay đổi điều kiện x > y thành x <= y, mô phỏng lỗi trong logic điều khiển. Sự kết hợp của các toán tử này cho phép tạo ra một tập hợp đột biến đa dạng, thách thức bộ kiểm thử ở nhiều khía cạnh khác nhau.

4.1. Nhóm toán tử đột biến biến và hằng số VRO CRO

Nhóm toán tử này tập trung vào các lỗi liên quan đến việc sử dụng sai dữ liệu. Toán tử thay thế biến (VRO - Variable Replacement Operator) hoạt động bằng cách thay thế một biến này bằng một biến khác cùng kiểu trong chương trình. Lỗi này rất phổ biến, ví dụ như lập trình viên gõ nhầm tên biến. Toán tử thay thế hằng số (CRO - Constant Replacement Operator) cũng hoạt động tương tự, thay thế một hằng số này bằng một hằng số khác. Cả hai toán tử này giúp phát hiện các lỗi liên quan đến việc khởi tạo hoặc tham chiếu dữ liệu sai.

4.2. Nhóm toán tử đột biến biểu thức số học và quan hệ ARO RRO

Đây là nhóm toán tử quan trọng nhất, nhắm vào logic tính toán và điều khiển. Toán tử thay thế toán tử số học (ARO - Arithmetic Replacement Operator) thay thế một toán tử số học (+, -, *, /, mod, div) bằng một toán tử khác trong cùng nhóm. Toán tử thay thế toán tử quan hệ (RRO - Relational Replacement Operator) thay thế một toán tử quan hệ (=, <, <=, >, >=, <>) bằng một toán tử khác. Những đột biến này rất hiệu quả trong việc kiểm tra các điều kiện biên và các nhánh logic của chương trình.

4.3. Nhóm toán tử đột biến biểu thức luận lý và một ngôi LRO UOI UOD

Toán tử thay thế toán tử luận lý (LRO - Logical Replacement Operator) thay thế các toán tử như and, or, xor với nhau. Toán tử chèn toán tử một ngôi (UOI - Unary Operator Insertion), cụ thể là chèn toán tử not, nhằm kiểm tra các trường hợp phủ định logic. Ngược lại, Toán tử xóa toán tử một ngôi (UOD - Unary Operator Delete), ví dụ như xóa toán tử thời gian pre(), giúp phát hiện các lỗi liên quan đến trạng thái và bộ nhớ trong các chương trình đồng bộ.

V. Ứng dụng công cụ sinh đột biến và đánh giá kết quả thực tiễn

Mục tiêu cuối cùng của việc xây dựng một công cụ tự động là để áp dụng vào thực tế và đánh giá hiệu quả của nó. Công cụ MuLustre đã được thử nghiệm trên một số lượng lớn các chương trình Lustre để kiểm chứng khả năng sinh đột biến và tính khả thi của phương pháp. Kết quả thực nghiệm được trình bày trong luận văn cho thấy công cụ hoạt động hiệu quả, có khả năng phân tích và tạo ra hàng trăm, thậm chí hàng nghìn đột biến một cách nhanh chóng và chính xác. Quá trình thử nghiệm không chỉ xác nhận rằng việc tự động hóa là khả thi mà còn cung cấp những dữ liệu quý giá về hiệu suất của các toán tử đột biến cho Lustre. Ví dụ, kết quả cho thấy các toán tử như ARO (thay thế toán tử số học) và RRO (thay thế toán tử quan hệ) có khả năng sinh ra số lượng đột biến lớn nhất, phản ánh sự phổ biến của các biểu thức này trong các chương trình thực tế. Sau khi sinh đột biến, bước tiếp theo là đánh giá chất lượng của một bộ dữ liệu thử ngẫu nhiên bằng cách tính điểm đột biến (mutation score). Kết quả cho thấy, ngay cả với bộ thử được sinh ngẫu nhiên, một tỷ lệ đáng kể các đột biến đã bị "diệt", chứng tỏ tiềm năng của phương pháp. Phân tích các đột biến còn "sống sót" cung cấp thông tin chi tiết để cải thiện bộ kiểm thử, giúp người kiểm thử tập trung vào việc tạo bộ kiểm thử mới để diệt các đột biến "cứng đầu" hơn, từ đó nâng cao chất lượng tổng thể của quy trình xác minh và thẩm định (V&V).

5.1. Kết quả thử nghiệm sinh đột biến tự động trên các chương trình Lustre

Công cụ MuLustre được thử nghiệm với nhiều chương trình Lustre có độ phức tạp khác nhau. Bảng kết quả trong luận văn (Bảng 3.2 và 3.3) cho thấy số lượng đột biến được tạo ra tương ứng với từng toán tử. Chẳng hạn, một chương trình đơn giản có thể tạo ra vài chục đột biến, trong khi các chương trình lớn hơn có thể tạo ra hàng trăm đột biến. Quá trình này, nếu thực hiện thủ công, sẽ mất rất nhiều giờ, nhưng với công cụ tự động, nó chỉ mất vài giây hoặc vài phút. Điều này chứng minh rằng tự động hóa giúp giảm đáng kể thời gian và chi phí trong giai đoạn chuẩn bị cho mutation testing for Lustre.

5.2. Phân tích điểm đột biến và hiệu quả của bộ dữ liệu thử

Chỉ sinh đột biến là chưa đủ; mục tiêu là sử dụng chúng để đánh giá và cải thiện bộ kiểm thử. Trong thử nghiệm, một bộ dữ liệu thử được sinh ngẫu nhiên và chạy trên các đột biến đã tạo. Điểm đột biến sau đó được tính theo công thức MS = D / (M - E), trong đó D là số đột biến bị diệt, M là tổng số đột biến, và E là số đột biến tương đương. Kết quả thực nghiệm (Bảng 3.4) cho thấy các tỷ lệ đột biến khác nhau cho các chương trình khác nhau, cung cấp một thước đo định lượng về chất lượng của bộ thử. Một điểm số thấp chỉ ra rằng bộ thử cần được cải thiện, trong khi một điểm số cao mang lại sự tin tưởng hơn vào tính toàn diện của nó.

VI. Tương lai của công cụ sinh đột biến tự động cho Lustre

Việc xây dựng thành công công cụ MuLustre đã mở ra một hướng đi đầy hứa hẹn cho việc áp dụng kiểm thử đột biến trong các dự án phát triển bằng ngôn ngữ lập trình Lustre. Đây là một đóng góp quan trọng, giúp tự động hóa một khâu tốn kém trong quy trình xác minh và thẩm định (V&V), đặc biệt là đối với các hệ thống an toàn-critical. Công cụ này không chỉ giúp giảm chi phí và thời gian mà còn nâng cao độ tin cậy của phần mềm bằng cách cung cấp một phương pháp có hệ thống để đánh giá và cải thiện chất lượng bộ kiểm thử. Tương lai của các công cụ sinh đột biến tự động cho Lustre nằm ở việc tích hợp sâu hơn vào các quy trình phát triển phần mềm hiện đại (DevOps, CI/CD) và kết hợp với các kỹ thuật xác minh khác. Một hướng phát triển tiềm năng là tích hợp khả năng phát hiện đột biến tương đương một cách tự động, có thể bằng cách sử dụng các kỹ thuật tối ưu hóa trình biên dịch hoặc các bộ giải ràng buộc (constraint solvers). Một hướng khác là kết hợp phân tích đột biến với các phương pháp xác minh hình thức (formal verification). Ví dụ, các đột biến còn "sống sót" sau khi kiểm thử có thể trở thành mục tiêu cho các công cụ kiểm tra mô hình (model checking) để chứng minh chúng có tương đương hay không. Cuối cùng, việc mở rộng bộ toán tử đột biến cho Lustre để bao quát nhiều loại lỗi hơn, đặc biệt là các lỗi liên quan đến đặc tính thời gian thực và đồng bộ của chương trình đồng bộ, cũng là một lĩnh vực nghiên cứu quan trọng.

6.1. Đóng góp vào quy trình xác minh và thẩm định V V phần mềm

Công cụ sinh đột biến tự động như MuLustre góp phần hoàn thiện quy trình V&V bằng cách cung cấp một thước đo khách quan về chất lượng của bộ kiểm thử. Thay vì chỉ dựa vào các độ đo bao phủ mã nguồn truyền thống (như bao phủ dòng lệnh hay nhánh), điểm đột biến cung cấp một chỉ báo mạnh mẽ hơn về khả năng phát hiện lỗi. Việc tự động hóa giúp kỹ thuật này trở nên khả thi để áp dụng thường xuyên trong chu trình phát triển, cho phép phát hiện sớm các điểm yếu trong bộ kiểm thử và cải thiện chúng một cách kịp thời.

6.2. Hướng phát triển Tích hợp xác minh hình thức và tối ưu hóa

Trong tương lai, các công cụ này có thể được cải tiến để trở nên thông minh hơn. Một hướng đi là sử dụng thông tin từ phân tích đột biến để tự động tạo bộ kiểm thử mới nhằm diệt các đột biến còn sống. Kỹ thuật này, được gọi là sinh dữ liệu thử dựa trên đột biến (mutation-based test data generation), có thể sử dụng các thuật toán tối ưu hóa hoặc giải thuật di truyền. Hơn nữa, việc tích hợp với các công cụ xác minh hình thức sẽ tạo ra một quy trình V&V toàn diện, kết hợp sức mạnh của cả kiểm thử (testing) và chứng minh (proving) để đảm bảo mức độ an toàn cao nhất cho các hệ thống phần mềm quan trọng.

04/10/2025
Luận văn thạc sĩ xây dựng công cụ sinh đột biến cho chương trình lustre

Trích đoạn nội dung tài liệu

Chương 1 giới thiệu tổng quan về kiểm thử đột biến và các kỹ thuật kiểm thử đột biến, tình hình nghiên cứu và ứng dụng của kiểm thử đột biến trong nước và trên thế giới. Giới thiệu Kiểm thử là một công đoạn rất quan trọng, quyết định đến việc đánh giá chất lượng của một sản phẩm phần mềm. Đây là một quá trình tìm lỗi được tiến hành trong tất cả các giai đoạn tạo nên một sản phẩm phần mềm và nó là sự đánh giá cuối cùng về các đặc tả, thiết kế, mã hóa… Mục đích của kiểm thử nhằm đảm bảo rằng tất cả các thành phần của phần mềm “ăn khớp”, vận hành như mong đợi và phù hợp với các tiêu chuẩn thiết kế. Kiểm thử không chứng minh được phần mềm là hết lỗi mà nó chỉ giúp cho người viết mã tìm ra và có biện pháp khắc phục càng nhiều lỗi, càng sớm, càng tốt.

Để thực hiện tốt kiểm thử, ngoài việc nắm vững các kỹ thuật kiểm thử điển hình, người kiểm thử cần xây dựng kế hoạch kiểm thử, chuẩn bị dữ liệu thử, tiến hành kiểm thử, viết báo cáo về kết quả kiểm thử, và cần biết quản lý toàn bộ công việc kiểm thử. Hiện nay, có rất nhiều kỹ thuật kiểm thử khác nhau đang được nghiên cứu, phát triển và ứng dụng. Các kỹ thuật kiểm thử được chia thành hai nhóm chính: các kỹ thuật kiểm thử hộp đen và các kỹ thuật kiểm thử hộp trắng. Với mục đích phát hiện lỗi, hoạt động kiểm thử phần mềm thường phải trải qua các bước chính: tạo dữ liệu thử, thực thi phần mềm trên dữ liệu thử và quan sát kết quả nhận được.

Trong các bước này, bước tạo dữ liệu đóng vai trò quan trọng nhất, bởi vì chúng ta không thể tạo ra mọi dữ liệu từ miền vào của chương trình, mà chỉ có thể tạo ra các dữ liệu thử có khả năng phát hiện lỗi cao nhất. Một dữ liệu thử được xem là “tốt” theo nghĩa nó có khả năng phát hiện ra lỗi cụ thể mà các dữ liệu thử khác không phát hiện ra. Ngược lại, một dữ liệu thử được xem là “không tốt” theo nghĩa nó không có khả năng phát hiện một lỗi nào cả, nhưng rất khó để xác định được bởi vì chúng ta không biết chương trình có lỗi hay không. Trong cả hai trường hợp, chúng ta không có cách nào để đo được hiệu quả của dữ liệu thử.

Điều quan trọng là phải biết liệu có tồn tại lỗi để kiểm thử hay không, nhưng nếu biết được điều này thì kiểm thử là dư thừa [8]. Để gỡ bỏ nghịch lý này, người ta sử dụng các tiêu chuẩn để cung cấp các yêu cầu cho chất lượng dữ liệu thử, và vì vậy, đưa ra một thước đo để đánh giá và cải tiến bộ dữ liệu thử. Ví dụ, các bộ dữ liệu thử được cải tiến lặp đi lặp lại cho đến khi chúng thực hiện tất cả các câu lệnh trong chương trình (kiểm thử câu lệnh), hoặc thực hiện tất cả các quyết định cho cả hai trường hợp đúng và sai (kiểm thử nhánh). Nếu phù hợp với tiêu chuẩn được xem xét thì dữ liệu thử 6 được xem là có chất lượng (đối với tiêu chuẩn đó), và có nhiều khả năng chỉ ra các lỗi hơn nếu chúng tồn tại.

Tuy nhiên, các tiêu chuẩn trên thường không tập trung vào nguyên nhân thất bại của chương trình – được gọi là các lỗi. Tiêu chuẩn chất lượng đột biến là một loại tiêu chuẩn mà cung cấp một thước đo để đánh giá tính hiệu quả của dữ liệu thử bằng cách cho thấy các dữ liệu thử có thể làm lộ ra tất cả các lỗi đơn giản có thể có của một chương trình (ví dụ, sự khác biệt một từ đơn hoặc thay thế tên biến sai), tương tự như kiểm thử câu lệnh cho thấy tính hiệu quả của dữ liệu thử bằng cách đảm bảo rằng mọi dòng lệnh đã được thực thi. Tuy nhiên, bộ dữ liệu thử có khả năng sẽ không thể xác định được tất cả các lỗi. Trong trường hợp như vậy, tiêu chuẩn chất lượng đột biến cung cấp một thước đo để xác định việc cải tiến bằng cách lựa chọn một bộ dữ liệu thử mới.

Ví dụ, một bộ dữ liệu thử phát hiện 80% lỗi sẽ cho phép tạo ra dữ liệu thử tập trung vào 20% còn lại. Thước đo này cho phép kiểm soát, đánh giá và cải tiến dữ liệu thử lặp đi lặp lại dựa trên cơ sở kiểm thử đột biến (mutation testing) [9]. Kiểm thử đột biến là một trong những kỹ thuật được phát triển rất sớm và đang được ứng dụng rộng rãi. Đây là kỹ thuật kiểm thử dựa trên lỗi nhằm cung cấp một tiêu chuẩn kiểm thử, được gọi là tỷ lệ đột biến.

Tỷ lệ đột biến được sử dụng để đánh giá tập dữ liệu thử theo khả năng phát hiện lỗi. Nguyên tắc cơ bản của kiểm thử đột biến là các lỗi được sử dụng nhằm biểu diễn các sai sót mà các lập trình viên thường phạm phải. Các lỗi như thế được chèn vào trong chương trình gốc cần được kiểm thử, bởi một sự thay đổi cú pháp, nhằm tạo ra tập các chương trình lỗi được gọi là các đột biến, trong đó mỗi đột biến chỉ chứa một sự thay đổi cú pháp. Để đánh giá chất lượng của một tập dữ liệu thử, các đột biến được thực thi trên tập các dữ liệu thử nhằm kiểm tra các lỗi được chèn vào có được phát hiện.

Năm 1971, Dick Lipton đề xuất phương pháp kiểm thử đột biến, sau đó lĩnh vực này được đánh dấu sự ra đời và phổ biến bởi DeMillo [8] và Sayward [9]. Kỹ thuật này đã đạt được những kết quả đáng kể. Hiện nay, có nhiều công trình nghiên cứu phát triển hơn nữa về khả năng ứng dụng và làm cho kiểm thử đột biến trở thành một kỹ thuật kiểm thử thực tế hơn. Lý thuyết kiểm thử đột biến Trước khi trình bày lý thuyết kiểm thử đột biến, chúng ta bắt đầu bởi một ý tưởng đơn giản để ước lượng số lượng cá có trong một cái hồ.

Một cách để thực hiện việc đó là chúng ta đánh dấu một số lượng cá và thả vào hồ (giả sử, 1000 con cá), sau đó đánh bắt một số cá và đếm số con cá có đánh dấu. Nếu chúng ta bắt được 1000 con cá và 100 trong số đó bị đánh dấu, như vậy 1/10 số cá trong hồ bị đánh dấu, khi đó số cá thực sự trong hồ có thể được ước lượng khoảng 10000 con. Nếu chúng ta bắt được tất cả các cá bị đánh dấu, chúng ta có thể cho rằng toàn bộ cá trong hồ đã bị đánh bắt. 7 Kỹ thuật kiểm thử đột biến được xây dựng dựa trên ý tưởng này.

Chúng ta đưa vào mã nguồn một số lỗi “bị đánh dấu”, sau đó tìm cách xác định chúng. Nếu chúng ta xác định được tất cả các lỗi này, “lưới” của chúng ta có thể cũng đã bắt được nhiều các loại “cá” khác, đó chính là các lỗi chưa biết. Khái niệm kiểm thử đột biến Kiểm thử đột biến là một kỹ thuật kiểm thử hộp trắng, hay còn gọi kỹ thuật kiểm thử cấu trúc. Nó là một công cụ hỗ trợ cho công việc kiểm thử, được tạo ra với mục đích kiểm tra, đánh giá bộ dữ liệu thử và giúp cho việc tạo ra các bộ dữ liệu thử có khả năng phát hiện lỗi của chương trình.

Trong khi thực hiện kiểm thử đột biến, chúng ta tạo ra các phiên bản lỗi của chương trình gốc bằng cách chèn lỗi vào mã nguồn của chương trình cần kiểm thử. Mỗi phiên bản chỉ chứa đúng một lỗi và được gọi là một đột biến (mutant). Mỗi đột biến được tạo ra bởi chỉ một sự thay đổi cú pháp trong chương trình gốc. Mỗi sự thay đổi cú pháp là một luật hay còn được gọi là một toán tử đột biến (mutation operator).

Các toán tử đột biến được định nghĩa sẵn để tạo ra sự thay đổi cú pháp dựa trên các lỗi mà các lập trình viên thường phạm phải. Ví dụ, thay một biến bởi một biến khác cùng kiểu, hoặc thay một toán tử số học bởi một toán tử số học khác,… Một cách cụ thể, đột biến P’ của chương trình gốc P là một chương trình tương tự với chương trình gốc P; P’ khác P do chỉ một thay đổi nhỏ về cú pháp trong chương trình. Chẳng hạn, trong Bảng 1.1, P là chương trình gốc, P’ và P” là các đột biến của P bằng cách thay đổi cú pháp trong phép toán số học, thay thế (s=a+b) bởi (s=a-b) đối với P’; thay (s=a+b) bởi (s=a*b) đối với P”. Các câu lệnh bị đột biến được đánh dấu bởi ký hiệu ∆.

Ví dụ minh họa các đột biến Chương trình gốc P Đột biến P’ Đột biến P’’ node sum(a,b:int) node sum(a,b:int) node sum(a,b:int) returns (s:int); returns (s:int); returns (s:int); let let let s=a+b; ∆ s=a-b; ∆ s=a*b; tel; tel; tel; Dựa trên tiêu chuẩn chất lượng đột biến, lần lượt các đột biến sẽ được thực hiện với một bộ dữ liệu thử để xác định có bao nhiêu đột biến thất bại (tức là cung cấp đầu ra không đúng cho đầu vào kiểm thử đó so với chương trình gốc). Thất bại càng nhiều, càng lớn thì bộ dữ liệu thử càng chất lượng. Mục đích của kiểm thử viên là tạo ra dữ liệu thử mới để cải tiến chất lượng của các dữ liệu thử hiện có. Một kết quả khá hữu ích của phương pháp này là việc cải tiến chất lượng của dữ liệu thử sẽ cải thiện sự tin tưởng của kiểm thử viên vào tính đúng đắn của chương trình gốc.

Cơ sở kiểm thử đột biến Kiểm thử đột biến hứa hẹn sẽ hiệu quả trong việc xác định dữ liệu thử thích hợp có thể được dùng để phát hiện các lỗi thực sự [10]. Tuy nhiên, số lượng các lỗi tiềm năng cho một chương trình nhất định là rất lớn, không thể tạo ra đột biến đại diện cho tất cả các lỗi. Vì vậy, kiểm thử đột biến truyền thống chỉ nhắm mục tiêu một tập con của các phiên bản lỗi, là những phiên bản gần với phiên bản đúng của chương trình với hy vọng rằng đó sẽ là đủ để mô phỏng cho tất cả các lỗi. Lý thuyết này dựa trên hai giả thuyết cơ bản: giả thuyết “lập trình viên giỏi” (competent programmer hypothesis - CPH) [8], [9] và giả thuyết “hiệu ứng liên kết” (coupling effect hypothesis - CEH) [8].

- Giả thuyết lập trình viên giỏi được giới thiệu đầu tiên vào năm 1978 do DeMillo và các đồng nghiệp của ông [8]. Giả thuyết này cho rằng thông thường các lập trình viên đều rất giỏi và họ sẽ không bao giờ viết ra các chương trình một cách tuỳ tiện, cẩu thả.

Nội dung được bảo vệ bản quyền — Tải xuống đầy đủ