GIỚI THIỆU
Đặt vấn đề
Phân tích và đặc tả yêu cầu là yếu tố quan trọng trong phát triển phần mềm, với các yêu cầu thường được mô tả dưới dạng ca sử dụng (use case - UC) Mỗi tài liệu đặc tả chứa mô hình UC, thể hiện kịch bản với các bước cần thiết để hệ thống hoàn thành nhiệm vụ Kịch bản bao gồm các thành phần như sự kiện, tương tác giữa các sự kiện, cùng với các biểu thức tiền điều kiện (precondition) và hậu điều kiện (postcondition), xác nhận (assertion) Tiền điều kiện và hậu điều kiện xác định các ràng buộc cần thỏa mãn khi bắt đầu và kết thúc nhiệm vụ, trong khi assertion mô tả thuộc tính mà kịch bản phải đáp ứng tại một thời điểm nhất định Cuối cùng, kịch bản được hiện thực hóa qua mã nguồn chương trình, như hình 1.1 minh họa cho phép chia hai số nguyên dương với tiền điều kiện là y khác không và hậu điều kiện là kết quả của phép chia x cho y.
Hình 1.1 Kịch bản biểu diễn phép chia hai số nguyên
Việc kiểm tra tính đúng đắn của kịch bản bao gồm việc xác minh các biểu thức tiền-hậu điều kiện và các assertion trong giai đoạn đặc tả hoặc mã nguồn Hiện nay, có nhiều phương pháp khác nhau được đề xuất để kiểm tra tính đúng đắn tại các giai đoạn thiết kế và mã nguồn, bao gồm kiểm chứng và kiểm thử Mỗi phương pháp đều có ưu nhược điểm riêng; trong đó, kiểm thử yêu cầu mã nguồn và ca kiểm thử, thường chỉ phát hiện lỗi về giá trị đầu ra mà không phát hiện lỗi vi phạm ràng buộc thiết kế Vi phạm ràng buộc thiết kế có thể dẫn đến lỗi hệ thống, đặc biệt khi tích hợp nhiều mô-đun, gây khó khăn trong việc xác định vị trí lỗi và làm tăng chi phí phát triển Các phương pháp kiểm chứng như chứng minh định lý và kiểm tra hình thức cung cấp một giải pháp khả thi để đảm bảo tính đúng đắn của kịch bản.
Precondition: y != 0 Postcondition: (x / y n && sum < n/2 * n, nhằm đảm bảo rằng tổng sum là hợp lệ, với thông báo "sum is " + sum khi điều kiện không được thỏa mãn.
Giả sử lập trình viên cài đặt chương trình với các assertion cho kịch bản trên như sau:
Bảng 4.1 Cài đặt chương trình cho kịch bản kiểm tra các assertion
public void tong(){ for (i = 0; i < N; i++) { sum += i;
} } public static void main(String[] args) {
Scanner input = new Scanner(System in );
AssertionDemo as = new AssertionDemo(); sum =6;
System out print("Moi ban nhap so N : ");
//@ assert AssertionDemo.sum != 0 ; as.tong();
System out println(AssertionDemo.sum);
//# assume AssertionDemo.sum > AssertionDemo.N && AssertionDemo.sum < AssertionDemo.N / 2 * AssertionDemo.N
Kết quả kiểm chứng cho kịch bản trong Bảng 4.1 được trình bày trong Hình 4.5, cho thấy mã aspect đã phát hiện vi phạm ràng buộc assertion là sum < n/2 * n Để khắc phục, người lập trình đã điều chỉnh chương trình bằng cách thay đổi phép gán thành sum = 0 trong Bảng 4.1, sau đó thực hiện lại quá trình kiểm chứng và đạt được kết quả đúng với các assertion được thỏa mãn, như thể hiện trong Bảng 4.7.
Hình 4.5 là kết quả kiểm tra các assertion cho kịch bản đƣợc cài đặt sai
Hình 4.6 Kết quả kiểm tra các assertion cho kịch bản cài đặt đúng
Kịch bản 3 mô tả chức năng rút tiền từ tài khoản ATM, yêu cầu số tiền rút không được vượt quá số dư hiện có Sau khi thực hiện giao dịch, tài khoản phải giảm số dư so với trước khi rút và số tiền còn lại phải lớn hơn hoặc bằng 0 Chi tiết cài đặt kịch bản này được trình bày trong Bảng 4.2.
Bảng 4.2 Cài đặt kịch bản mô tả chức năng rút tiền từ tài khoản public class BankAccount { static int oldBalance =0; static int balance = 0; static int key, nap, rut;
//@ assert BankAccount.rut > BankAccount.balance ; static int debit(int amount){ balance -= amount; return balance;
//# assume BankAccount.balance > 0 && BankAccount.balance + BankAccount.rut == BankAccount.oldBalance ; static int credit(int amount){ balance += amount; return balance;
} public static void main(String[] args) { Scanner input = new Scanner(System in );
System out println("So du hien tai la:" + BankAccount.balance); do{
System out println("1: Nap them tien");
System out println("2: Rut tien");
System out println("3: Kiem Tra tai khoan");
System out println("4: Thoat"); key = input.nextInt(); switch (key){ case 1:{
System out print("Nhap so tien muon nap:"); nap = input.nextInt();
System out println("So tien trong tai khoan la:" + BankAccount.credit(nap)); break;
System out print("Moi ban nhap so tien muon rut:"); rut = input.nextInt();
System out println("So tien trong tai khoan la:" + BankAccount.debit(rut)); break;
System out println("So tien trong tai khoan la:"+ BankAccount.balance); break;
Sử dụng chương trình thực nghiệm của luận văn để kiểm chứng kịch bản này như sau:
1 Đọc kịch bản cùng với các biểu thức tiền-hậu điều kiện vào chương trình, Hình 4.7, bên trái,
2 Thực hiện sinh mã aspect, Hình 4.7 bên phải,
3 Kết quả đan xen mã và kiểm chứng như trong Hình 4.8 và 4.9
Hình 4.7: Mô tả tạo Aspect với bài toán rút tiền đơn giản
Hình 4.8: Kết quả đan xen mã
Hình 4.9: Kết quả kiểm chứng
Kịch bản 4 mô tả chức năng chuyển khoản của máy ATM, trong đó số tiền cần chuyển phải nhỏ hơn hoặc bằng số dư trong tài khoản chuyển Các assertion xác nhận rằng số tiền trong tài khoản nhận sẽ bằng tổng số tiền hiện có cộng với số tiền được chuyển, đồng thời tổng số tiền của các tài khoản trong hệ thống sẽ không thay đổi Chi tiết về cài đặt kịch bản này cùng với các biểu thức tiền điều kiện và assertion được trình bày trong Bảng 4.3.
Table 4.3 outlines the setup for a transfer function scenario in an ATM program, utilizing Java's Scanner for input The class ATM_Transfer defines static byte variables A0, B, and oldB, with a total byte variable Tong calculated as the sum of A and B Additionally, it initializes integer variables for transferMoney and a key to facilitate the transaction process.
//@ assert ATM_Transfer.A >= ATM_Transfer.transferMoney ; void tranfer(){ oldB = B;
System out println("So du hien tai cua cac tai khoan sau khi chuyen A = "+ A +" , B = " + B);
//# assume ATM_Transfer.B == (ATM_Transfer.oldB + ATM_Transfer.transferMoney) && ATM_Transfer.A + ATM_Transfer.B == ATM_Transfer.Tong ; public static void main(String[] args) {
ATM_Transfer ct = new ATM_Transfer();
Scanner input = new Scanner(System in );
System out println("So du hien tai cua cac tai khoan A = "+ A +" ,
System out print("Moi ban nhap so tien muon chuyen : "); transferMoney = input.nextInt(); ct.tranfer(); input.close();
Thực hiện sinh mã aspect và kiểm chứng cho kịch bản trong Bảng 4.3 với các
Bảng 4.4: Kết quả kiểm chứng cho kịch bản 4
So du hien tai cua cac tai khoan A = So du hien tai cua cac tai khoan A =
Moi ban nhap so tien muon chuyen :
AspectJava@12b3a41 Before call : tranfer Tien dieu kien dung
So du hien tai cua cac tai khoan sau khi chuyen A = 90 , B = 20
After call : tranfer Ket Qua Dung
Moi ban nhap so tien muon chuyen :
Aspect instance is created: AspectJava@12b3a41
ATM_Transfer.tranfer() In file: ATM_Transfer.java At line: 32
So du hien tai cua cac tai khoan sau khi chuyen A = -20 , B = -126
ATM_Transfer.tranfer() In file: ATM_Transfer.java At line: 32
Kết quả thử nghiệm cho thấy bộ công cụ kiểm chứng luận văn có khả năng phát hiện một số ràng buộc của kịch bản trong quá trình thực thi Các vi phạm được xác định chính xác tại vị trí gây ra lỗi Tuy nhiên, chương trình thực nghiệm vẫn còn một số hạn chế cần khắc phục.
- Số kịch bản thử nghiệm cho công cụ còn ít, chưa thực sự đánh giá được tính hiệu quả của công cụ được luận văn xây dựng,
- Chưa kiểm chứng được các ràng buộc khác của kịch bản như bất biến (invariants), xác nhận (assertion),
- Việc đan xen mã aspect cùng với mã nguồn java sẽ tốn thời gian thực thi chương trình.