Đề tài: Môn Nguyên Lý Lập Trình 1
PHẦN I
LÝ THUYẾT VỀ KIỂM CHỨNG CHƯƠNG TRÌNH.
I. CÁC MÔ HÌNH PHÁT TRIỂN PHẦN MỀM
1. Mô hình phần mềm
Để khái quát hóa lý thuyết kiểm chứng chương trình trước tiên
chúng ta tìm hiểu các mô hình phát triển phần mềm cơ bản thể hiện
các bước phát triển phần mềm và trong đó có tầm quan trọng của
giai đoạn kiểm chứng chương trình.
Mô thác nước truyền thống.
Mô hình có mối quan hệ song song
Để tránh các khuyết điểm trong quá trình phát triển phần
mềm do không có liên kết chặt chẽ giữa các khâu trọng yếu thì mô
hình mới ra đời như sau còn gọi là (Defect Prevention)
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 1
Đề tài: Môn Nguyên Lý Lập Trình 1
Mô hình V-MODEL
Mô hình V-MODEL static test
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 2
Đề tài: Môn Nguyên Lý Lập Trình 1
Mô hình V-MODEL Dynamic Test
Trong tất cả các mô hình phát triển phần mềm trên thì trong đó giai
đoạn kiểm chứng chương trình (test) là rất quan trọng quyết định đến
giá trị của phần mềm được sản xuất ra.
2. GIỚI THIỆI VỀ KIỂM CHỨNG CHƯƠNG TRÌNH
Kiểm chứng chương trình là một trong những bước quan trọng trong
quy trình phát triển sản phẩm phần mềm. Đảm bảo cho chương trình
phần mềm chạy ổn định tránh nhưng sai sót chủ quan khi đưa vào ứng
dụng thực tế. Vậy để thực hiện tốt việc kiểm chứng chương trình sao
cho có hiệu quả và có quy trình rõ ràngcần có các nguyên lý và tính chất
sau:
II. PHẨM CHẤT CỦA CHƯƠNG TRÌNH.
Các chương trình phần mềm khi được phát triển và đưa vào ứng dụng
thực tế cần phải đạt được những phẩm chất cơ bản sau:
- Tính đúng đắn, chính xác (Correctness)
- Tính chắc chắn (robustness)
- Tính thân thiện với người dùng (User friendliness)
- Khả năng thích nghi (adapability):chương trình phần mềm có khả
năng thích ứng và tiến hóa theo yêu cầu chung phù hợp với thực tế
đưa ra.
- Tính tái sử dụng (reuseability): chương trình có thể dùng để làm một
phần trong một chương trình khác lớn hơn.
- Tính tương liên (Interoperability)
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 3
Đề tài: Môn Nguyên Lý Lập Trình 1
- Tính hiệu quả (efficiency)
- Tính khả chuyển (porability): có khả năng chuyển đổi dễ dàng giữa
các môi trường phát triển khác nhau.
- Tính an toàn hệ thống (Security).
Ngoài 9 phẩm chất trên còn có phẩm chất quan trọng nữa là tính dừng
của chương trình.
Năm 1985-1986 LENARD nói rằng “ Không thể tồn tại một chương
trình kiểm tra tính dừng của một chương trình”.
II. CÁC PHƯƠNG PHÁP KIỂM CHỨNG CHƯƠNG TRÌNH
1. Cấu trúc kiểm chứng chương trình
Có các phương thức chính để kiểm chứng tính đúng đắn của chương
trình dưa trên các công trình nghiên cứu đã đưa ra như sau:
- Kiểm chứng toàn bộ các nhánh rẽ của chương trình. Sao cho mỗi
lệnh của chương trình đều phải qua ít nhất một lần (Black-Box).
- Tạo bộ dữ liệu thử nghiệm ngẫu nhiên để phát hiện ra lỗi giả hay lỗi
thật.
- Kiểm tra ỡ những điểm nút
- Chèn các lệnh kiểm tra logic ở mỗi đoạn chương trình.(White -Box)
2. Tính đúng đắn của chương trình
Khi chương trình thực hiện mà xảy ra sai sót thì có rất nhiều nguyên
nhân nhưng có 3 nguồn gốc xảy rai sót chính là.
- Dữ liệu đầu vào: do dữ liệu nhập vào ban đầu thiếu tính ràng buộc
nên sẽ xảy ra sai sót khi thực hiện khi đó để kiểm chứng thì dùng bộ
kiểm tra dữ liệu ngẫu nhiên, hay đặc trưng tuỳ ý.
- Cú pháp : Dùng trình biên dịch để phát hiện
- Ngữ nghĩa: Lỗi này rất khó phát hiện dùng test logic, Heuristic test
để kiểm tra.
Mỗi chương trình đều có ràng buộc về tính đúng đắn đối với điều
kiện input đầu vào.
∀x,P(x) : điều kiện đối với x
Q(y): điều kiện đối với y
Ta có : P(x)^Program(x,y) Q(y)
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 4
Input: a,b,c
Program
(ax
2
+bx+c=0)
Output: x1,x2
∀a,b,c,P(a,b,c):
b
2
-4ac>=0
X1,x2:Q(ax
1
2
+bx
1
+c=0,a
x
2
2
+bx2+c=0)
Đề tài: Môn Nguyên Lý Lập Trình 1
Ví dụ 1:
Kiểm chứng chương trình giải phương trình bậc 2
Ta có phân tích sơ đồ kiểm chứng như sau:
Ví dụ 2:
Kiểm chứng tính đúng đắn của chương trình tính thương và dư số
a/b với chương trình như sau:
r:=a;q:=0;
While r>b do
Begin
r:=r-1;
q:=q+1;
end;
Kết quả: q: là thương số, r : là dư số.
Kiểm tra :
- Điều kiện của phép chia b<>0. Nếu b=0 vòng lặp sẽ không dừng
được dẫn đến chương trình sai.
- Các cận : với a=b dẫn đến kết quả sai vì dứ số r=x và thương q=0.
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 5
Input:x Program(x,y) Output:y
∀x,P(x)
Q(y)
Đề tài: Môn Nguyên Lý Lập Trình 1
Chương trình đúng được viết lại như sau :
r:=a;q:=0;
While r>=b do
Begin
r:=r-1;
q:=q+1;
end;
3. Tính chắc chắn của chương trình
Cần phân tích chương trình thành các chương trình con sao cho tính
độc lập giữa các chương trình con càng cao càng tốt.
Ví dụ :
i :=n
While i>0 do
Begin
Read(a[i]);
i:=i-1;
end;
for j:=1 to n do
i:=i+a[j];
writeln(‘tong so=’,i);
Chương trình sẽ thực hiện sai nếu n<0 do biến i được dùng chung
cho cả hai vòng lặp.
4. Kiểm chứng chương trình với mô hình nghịch đảo
Để kiểm tra tính logic của chương trình ta có thể sử dụng phương
pháp nghịch đảo để kiếm chứng lại tính đúng đắn thực tế của chương
trình:
Ví dụ 1: Thuật toán nhân Ấn Độ được phát triển như sau:
Input: x,y
Output: z=x*y
Thuật Toán :
Nhập x,y
If x le then
z=y;
Else
z=0
End if
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 6
Đề tài: Môn Nguyên Lý Lập Trình 1
While x>1 do
Begin
x:=x div 2;
y:=y*2;
If x lẻ then z:=z+y;
End;
z:=z+y;
Để kiểm chứng chương trình trên ta lập trình lại và chèn thêm dòng
lệnh kiểm chứng nghịch đảo để kiểm chứng chương trình như sau :
Promgram nhan_ando ;
var
x,y,k,m,n :word ;
BEGIN
repeat write(‘nhap gia tri x=’) ;readln(x) until x>=0 ;
repeat write(‘nhap gia tri y=’) ;readln(y) until y>=0 ;
m :=x ;
n :=y ;
if (x mod 2)=1 then k:=y; else k :=0 ;
if y<>0 then
begin
while x>1 do
begin
x :=x shr 1 ;
y :=y shl 1 ;
if ((x mod 2)=1) then k :=k+y ;
end ;
k :=k+y ;
writeln(‘x*y=’,k) ;
(* chèn thêm đoạn lệnh kiểm chứng*)
if ((k/n)=m) and ((k/m)=n) then
write(‘Thuat toan dung’) ;
else
writeln(‘thuat toan sai !’) ;
readln ;
end ; (* END OF IF*)
END ;
5. Kiểm chứng chương trình dùng phương pháp quy nạp.
Phương pháp này dựa vào phương pháp chứng minh quy nạp trong
toán học. được phát biểu như sau :
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 7
Đề tài: Môn Nguyên Lý Lập Trình 1
Để chứng minh một tân từ P(n) phụ thuộc vào số tự nhiên là đúng
với mọi n. thì ta cần chứng minh hai điều sau đây :
(i) P(0) là đúng.
(ii) Nếu P(n) được giả định là đúng thì sẽ suy ra P(n+1) cũng
đúng.
Khẳng định P(0) được gọi là cơ sở và bước chứng minh (ii) là bước
quy nạp. Khi đã có hai điều chứng minh (i) và (ii), đựa vào nguyên
lý quy nạp toán học ta kết luận P(n) đúng với mọi số tự nhiên n.
Ngoài ra còn có phương pháp chứng minh quy nạp mạnh được phát
biểu như sau :
Để chứng minh P(n) đúng với mọi số tự nhiên ta chứng minh hai
điều sau :
(i) P(0) đúng
(ii) Nếu giả định là P(0),P(1), P(n) đều đúng thì P(n+1) cũng
đúng.
Ví dụ 1 :
Đầu vào là x,y,n để có đầu ra là x=x.yn ta viết đoạn chương trình
tính như sau :
while n<>0 do
begin
x :=x*y ;
n :=n-1 ;
end ;
ketqua :=x ;
Hãy chứng minh đoạn chương trình trên thực hiện đúng ý đồ của người
viết.
Xét vị từ S(n) cho đoạn chương trình sau :
‘’∀ x,y ∈ R, n ∈ N : nếu đầu vào là x,y,n thì đầu ra là : x=x*y
n
‘’
ta chứng minh bằng nguyên lý quy nạp :
Khi n=0 thì vòng lặp không thực hiện được, ketqua cua đoạn chương trình
là x, vậy S(0) đúng.
Ta giả sử S(k) đúng, tức đầu ra là x=x*y
k
Ta cần chứng minh S(k+1) đúng :
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 8
Đề tài: Môn Nguyên Lý Lập Trình 1
Thật vậy do k>=0 ==> k+1>0 nền vòng lặp sẽ thực hiện ít nhất là thêm 1
lần nữa suy ra x=x*y=x*y
k
*y = x=y
k+1
nghĩa là S(k+1) đúng
Vậy đoạn chương trình trên là đúng với ý đồ của người lập trình.
Ví dụ 2 :
Đầu vào là x,y,n để có đầu ra là x=x+n*y ta viết đoạn chương trình như
sau :
While n<>0 do
begin
x :=x+y ;
n :=n-1 ;
end ;
ketqua :=x ;
Hãy chứng minh đoạn chương trình trên đúng với ý đồ của người viết.
Ta xét vị từ S(n) cho đoân chương trình trên như sau :
‘’ ∀ x,y ∈ R, n ∈ N : nếu đầu vào là x,y,n thì đầu ra là : x=x+n*y ‘’
Chứng minh bằng quy nạp :
Với n=0 thì vòng lặp không thực hiện được chương trình thực hiện có kế
quả = x+0*y=x vậy S(0) đúng.
Ta giả sử S(k) đúng tức đầu ra của đoạn chương trình là x=x+k*y.
Ta cần chứng minh S(k+1) đúng.
Thật vập do k>=0 ==> k>0 nên vòng lặp sẽ thực hiện thêm ít nhất là một
lần nữa suy ra x=x+y=x+k*y+y=x+(k+1)*y nghĩa là S(k+1) đúng.
Vậy đoạn chương trình trên là đúng với ý đồ của người viết.
Đối tượng của những phương pháp kiểm chứng logic là tìm ra những
bằng chứng tổng quát sao cho bạn không cần theo dõi hết mọi điều kiện có
thể. Thực hiện điều này liên quan đến một vài dạng đại diện logic bằng biểu
tượng và các kỹ thuật thực hiện những phép suy luận logic. Một trong
những kỹ thuật mạnh nhất cho mục đích này là phương pháp toán học gọi
là phép qui nạp. Phương pháp này áp dụng cho những biểu thức logic với
những tham số nguyên. Nó có tình trạng như sau :
1. Nếu biểu thức f (n) có thể được chứng minh là đúng đối với một giá
trị n
0
nào đó
2. Khi n > n
0
và khi f (n) đúng, f (n + 1) cũng được chứng minh là đúng
3. Thì f (k) đúng với mọi k >= n
0
Sử dụng khái niệm này có thể tiết kiệm nhiều thời gian trong kiểm tra
logic. Bạn bắt đầu bằng cách chứng minh rằng logic của một chương trình
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 9
Đề tài: Môn Nguyên Lý Lập Trình 1
(ở đây là một biểu thức logic) là đúng cho trường hợp cơ bản. Sau đó bạn
chứng minh rằng nếu logic này đúng cho một giá trị n lớn hơn nào đó thì nó
cũng đúng cho n + 1, điều này là một bằng chứng đầy dủ cho mọi giá trị lớn
hơn của n và bạn không cần tìm thêm một bằng chứng nào nữa.
Đây là một nguyên lý mạnh mẽ, nó phải được quan tâm sử dụng. Giả
sử rằng bạn đang thực hiện một vòng lặp như sau:
For i = 1 to 1000
BEGIN
Do x y z
END
Nếu bạn kiểm tra và chứng thực rằng logic hợp lý ở những giá trị i = 1
, i =5 , i = 6 bạn có thể kết luận rằng nó đúng với mọi giá trị cao hơn của i.
Tuy nhiên rõ ràng ở đây có một vấn đề khi mà i = 1001. Bài toán ở đây là
cách mà kiểm chứng được thực hiện. Sau khi cho rằng logic là đúng ở i = 1,
kế đến bạn cần kiểm tra trường hợp i = n. Bạn cho rằng logic là đúng khi
giá trị i = n, nó cũng đúng cho i = n + 1. Chú ý rằng bạn không có chứng
minh nó đúng cho i = n ; bạn giả sử điều này. Khảo sát bài toán, bạn có thể
thấy rằng logic sẽ không làm việc khi n = 1000. Để tránh điều này bạn nên
thực hiện cuộc kiểm tra với logic được sửa đổi như sau:
For i = 1 to limit
BEGIN
Do x y z
END
Đầu tiên bạn chứng thực rằng hàm logic đúng ở limit = 1 ; kế đến bạn
chứng minh rằng nếu đúng với limit = n thì nó cũng đúng cho limit = n + 1.
Khi đó nó cũng đúng với mọi giá trị cao hơn của limit. Ở đây n là một giá
trị nguyên dương. Lại nữa bạn cũng cần bảo đảm rằng không có những giá
trị phi số trong hàm xyz nó có thể làm sai lệch các cuộc thực nghiệm này.
Một cách khác để tiếp cận nguyên lý này là xem có thể tìm thấy bất kỳ một
giá trị n hay không, nơi mà chương trình thì đúng ở limit = n nhưng không
đúng ở limit n + 1. Những vấn đề như thế được gây nên bởi những ràng
buộc về bộ nhớ hoặc là kích thước của hệ thống số
6. Thực hiện bằng biểu tượng
Logic sau đây đưa ra bởi Mills cho thấy thực hiện bằng biểu tượng làm
việc như thế nào trong trường hợp đơn giản
Procedure Substitution (V1, V2, V3, V4)
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 10
Đề tài: Môn Nguyên Lý Lập Trình 1
BEGIN
V1 : = V2 ;
V2 : = V4 ;
V3 : = V1 ;
V4 : = V3 ;
END
Bảng sau cho ta kết quả. Logic này trông đơn giản nhưng logic đơn
giản này dường như trở thành rất phức tạp khi nó liên quan đến tiến trình tuần
tự. Ưu điểm của kiểm chứng bằng biểu tượng là nó bao phủ hết tất cả giá trị
đặc trưng hơn là một số các trường hợp được test
Chu kỳ Function: Substitution (V1, V2, V3, V4)
# Lệnh V1 V2 V3 V4
Trị khởi động A B C D
1 V1 : = V2 B
2 V2 : = V4 D
3 V3 : = V1 B
4 V4 : = V3 B
Giá trị sau
cùng
B D B B
Không có những hướng dẫn tổng quát nào cho việc là khi nào và làm thế
nào để sử dụng sự thực thi bằng biểu tượng, tuy nhiên bạn nên kiểm tra chương
trình của mình và sử dụng điều này trong bất kỳ trường hợp nào mà bạn thấy
trong thực tế. Một trong những điểm hay nhất của những phương pháp kiểm
chứng bằng toán học là nó cung cấp một cách được cấu trúc hóa để kiểm chứng
một chương trình một cách logic.
7. CHỨNG THỰC ĐỐI TƯỢNG STATE MACHINE
Khi mà sản phẩm của bạn có một state machine, bạn nên chứng
minh rằng nó được thiết kế một cách hợp lý và sử dụng một cách đúng
đắn .Để quyết định một chương trình có là state machine hay không cần
kiểm tra hành xử bên ngoài của nó. Một ví dụ là hành xử của bộ đọc ký
tự đối với bộ đếm .Bộ đọc sẽ gửi tất cả ký tự tới bộ đếm.Tuy nhiên, khi
nó phát hiện bắt đầu một dòng chú thích, nó sẽ bỏ qua tất cả những ký
tự theo sau cho đến cuối dòng chú thích. Ở đây thì bộ đọc ký tự có hai
trạng thái: dòng chú thích và dòng chương trình. Với những ký tự rõ
ràng là input, trong một trường hợp nó không tạo ra output và trong
trường hợp còn lại nó sẽ gởi các ký tự input ra output, khi một state
machine được thiết kế đúng đắn thì nó phải có khả năng trả ra cho
chương trình những trạng thái mà mọi điều kiện trạng thái phải đầy đủ
và trực giao với nhau, bạn nên kiểm tra những ngữ cảnh lớn hơn của
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 11
Empty State
Member State
n > 0
Pop or Subtract
Add or Push or Pop ( n >1 ) or
Subtract ( D không thuộc Bset ) or
Subtract ( n >1 )
Push or Add
Pop ( n = 1) or
Subtract ( n = 1 và D thuộc Bset )
Đề tài: Môn Nguyên Lý Lập Trình 1
chương trình để đảm bảo rằng tất cả các state machine được định ra rõ
ràng và được sử dụng một cách nhất quán.
Người ta định nghĩa một tập các hàm là đầy đủ nghĩa là hội của
mọi thành viên là đúng. Ví dụ như là một tập đầy đủ các hàm sẽ phủ
mỗi hình vuông trên một bảng đồ Kanaugh. Một tập hàm gọi là trực
giao nghĩa là mọi phần giao của các thành viên là rỗng hoặc bằng zero.
Một tập các hàm trực giao không gối chồng lên nhau trên bảng đồ
Kanaugh. Do đó, với một tập trực giao các chuyển dịch về trạng thái,
mỗi một chuyển dịch là duy nhất và không có sự nhầm lẫn về trạng thái
kế tiếp. Để kiểm tra điều này thì một state machine thì thích hợp hơn và
theo những bước như sau :
• Kiểm tra cấu trúc của state machine để bảo đảm rằng không có
những cái bẫy tìm ẩn hoặc các vòng lặp để nó không thể dính vào
trong một vòng lặp vô hạn nào đó và không bao giờ trả về cho ta
một tình trạng được
• Kiểm tra thiết kế chương trình để bảo đảm rằng mọi tình trạng có
thể được phân định rõ. Nó có đầy đủ hay không ? Một thiết kế
chương trình là đầy đủ khi mà một trạng thái được định nghĩa
cho mỗi cái nối kết có thể của những giá trị thuộc tính.
• Kiểm tra tính trực giao của tình trạng, đó là, đối với mọi tập các
điều kiện thì chỉ có duy nhất một và chỉ một trạng thái có thể
• Kiểm chứng rằng các chuyển dịch từ mỗi trạng thái là đầy đủ và
trực giao, đó là, từ mỗi một trạng thái một tình trạng duy nhất kế
tiếp được định nghĩa cho mọi nối kết có thể của những giá trị
input
Hai phần sau đây sẽ cho ta những ví dụ làm thế nào để kiểm tra tính
đúng đắn của state machine. Thứ nhất là state machine đơn giản Bset,
thứ hai là Cdata
Kiểm tra Bset và State Machine
Mẫu mô tả trạng thái Bset được cho như sau
8. Kiểm tra Bset và State Machine
Mẫu mô tả trạng thái Bset được cho như sau
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 12
Đề tài: Môn Nguyên Lý Lập Trình 1
Dòng trên cùng mô tả trạng thái rỗng và 2 dòng ngay bên dưới cho
trạng thái kế từ trạng thái rỗng
Khi Bset là Empty State các thủ tục Pop hoặc Subtract được gọi.
Dòng kế tiếp Member State, chỉ ra rằng khi Bset trong trạng thái Empty
sẽ trở thành Member State khi hoặc là Push hoặc là Add được gọi.
Trong Member State, điều kiện thì phức tạp hơn một chút. Member
State sẽ trở thành Empty khi thủ tục Pop được gọi và chỉ có một thành
phần duy nhất trong Bset ( n = 1). Nó cùng kết thúc trong Empty
State khi Bset có một thành viên D và thủ tục Subtract được gọi với
tham số D
Empty State Tập không có thành viên n = 0
Empty State
Pop ( & D ) ∨ Subtract ( D )
Member State
Push ( D ) ∨ Add ( D )
Member State Tập có 1 hoặc nhiều thành viên n >= 1
Empty State
Pop ( & D ) ∧ ( n = 1 ) ∨
[ ( Subtract ( D ) ∧ ( n = 1 ) ∧ D ∈ Bset ) ]
Member State
Push ( D ) ∨ Add ( D ) ∨ [ Pop ( & D ) ∧ ( n >
1) ] ∨
[ Subtract ( D ) ∧ D∉ Bset ] ∨ [ Subtract ( D ) ∧
( n > 1 ) ]
Các phương thức được trình bày như sau
• Push ( D ) : thêm tập D vào Bset ở vị trí thứ nhất, đẩy mọi thành
viên đang tồn tại lên 1 vị trí
• Pop ( D ) : xóa từ vị trí đầu tiên của Bset, đặt vào trong D, và
di chuyển mọi thành viên trong Bset xuống 1 vị trí
• Add ( D ) : nếu D không là thành viên của Bset, Push ( D )
• Subtract ( D ) : nếu D là thành viên của Bset, xóa nó và giảm đi một
vị trí của tất cả thành viên cao hơn của Bset
Để kiểm tra xem State machine có hoạt động tốt hay không hãy hỏi các câu
hỏi sau đây
1.Bset có ẩn chứa vòng lặp hay không ?
2.Bset có là State machine đầy đủ không ?
Đối với Bset chỉ có một thuộc tính mà ta quan tâm là n : số các
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 13
Đề tài: Môn Nguyên Lý Lập Trình 1
thành viên trong Bset
Có 2 trạng thái của Bset là N = 0 và n >= 1
3.Các trạng thái của Bset có trực giao hay không ?
Rõ ràng là trực giao vì không thể có n = 0 và n ≠ 0 đồng thời
4.Các chuyển dịch từ mỗi trạng thái có đầy đủ và trực giao hay
không ?
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 14
EmptySet
List State = = 0
List Position = = 0
First and Only
List State = = 1
List Position = = 1
First of Several
List State = = 2
List Position = = 1
1
6
2
12
8
13
7
11
Middle of Several
List State = = 3
1 < List Position < N
List of Several
List State = = 4
List Position = = N
16
22
21
14
18
23
19
25
20
24
Đề tài: Môn Nguyên Lý Lập Trình 1
Kiểm tra các bẫy tiềm ẩn hoặc các vòng lặp
Hình vẽ này được xây dựng từ mẫu trạng thái và trình bày mỗi chuyển dịch trong
số các trạng thái.
Ví dụ : EmptySet chuyển dịch tới First & Only theo điều kiện 2. Điều kiện 2 xảy ra
khi hoặc là phương thức Push hoặc AddSet được gọi.
Hình trên cũng cho ta thấy rằng State machine này không có bẫy hoặc các vòng lặp
tiềm ẩn, nó có thể ở bất kỳ trạng thái nào và chuyển tới bất kỳ trạng thái khác.
Ví dụ từ trạng thái LastOfSeveral có thể đến EmptySet theo điều kiện 21. Tương tự trạng
thái Empty State có thể chuyển dịch tới LastOfSeveral bởi dãy input 2, 8, 15.
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 15
Đề tài: Môn Nguyên Lý Lập Trình 1
Kiểm tra mọi trạng thái có thể
Kiểm tra mọi trạng thái có thể liên quan việc phân định tất cả các điều kiện có thể
của hệ thống. State machine liên quan đến tập dữ liệu chứa một số thành viên và một
pointer trỏ đến một trong các thành viên này.
Trong ví dụ State machine, chỉ có 5 trạng thái là có ích đối với chương trình
đang được thiết kế. Những trạng thái này là hệ thống có không, một hoặc vài số
thành viên lớn hơn và các pointer không trỏ đến gì cả, đến thành viên đầu tiên hoặc
thành viên ở giữa, hoặc thành viên cuối cùng. Các trạng thái là như sau:
EmptySet , ListState = 0 ; pointer không trỏ gì cả vì vậy ListPosition = 0
First & Only , ListState = 1 ; chỉ duy nhất một thành viên, pointer phải trỏ đến nó,
vì vậy ListPosition = 1
First of Several , ListState = 2 ; có một vài thành viên, pointer trỏ đến cái đầu tiên,
vì vậy
ListPosition = 1
Middle of Several , ListState = 3 ; khi pointer trỏ đến thành viên ở giữa, phải có ít
nhất 3 cái. Do đó listpointer lớn hơn 1 và bé hơn N, với N là số thành viên trong tập
hợp
LastOfSeveral , ListState = 4 ; phải có 2 hoặc nhiều thành viên hơn với pointer trỏ
đến cái cuối cùng, vì vậy ListPosition = N
9. Kiểm tra tính trực giao của trạng thái
Để xác định trạng thái là trực giao, bạn cần xác định 2 trạng thái bất kỳ tồn tại dưới
cùng một tham số. Bằng cách kiểm tra giá trị của ListState và ListPosition, bạn có thể
thấy rằng giá trị của ListState là duy nhất cho mỗI một kết nối của N và các tham số
ListPosition. Vậy thì, với mọi kết nối của N và ListPosition có một và chỉ một giá trị
ListState. Trạng thái như thế gọi là trực giao
III. LƯU VẾT CHƯƠNG TRÌNH :
Có nhiều cách khác nhau để kiểm chứng Logic Specification Template trước khi bạn
cài đặt hoặc test một chương trình. Nó bao gồm bảng thi hành, bảng lưu vết và kiểm
chứng bằng toán học. Phần này và các mô tả theo sau sẽ minh họa các tiếp cận làm
việc như thế nào.
• Bảng thi hành (Execution Table)
Bảng thi hành cung cấp một cách thứ tự để kiểm tra dòng logic trong LST.
Phương pháp này tốn kém về thời gian đặc biệt đối với các chương trình dài và phức
tạp nhưng rất đáng tin cậy và là phương pháp kiểm chứng đơn giản. Ta có đoạn
chương trình sau
Clear Space ( Input State )
1. Length = length ( Input )
State = 0
2. Repeat until ( State = 3 or length = 0 )
3. if Input [ length ] = ‘ ‘ { 1 ký tự trắng ở cuốI chuỗi Input }
4. length = length – 1
5. if State < 2
State = State + 1
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 16
State = 0
Non space
State = 2
State = 1 State = 3
Non space
Non space
space
space
space
Đề tài: Môn Nguyên Lý Lập Trình 1
6. else State = 3
until ( State = 3 or length = 0 )
7. if length > 0
8. for N = 1 to length do
9. if ( Input [ N ] < > ‘ ‘ or First )
10. Output = Output + Input [ N ]
First = true
11. Input = Output
Clear Space dùng để xóa các khoảng trắng ở đầu và cuối một chuỗI ký tự và nó
trả về chuỗi đã xóa các khoảng trắng. Hàm sẽ trả về một giá trị State
Clear Space Logic Specification Template được minh họa như sau
• Khi input là chuỗi rỗng, trả về emptystring với state = 0
• Khi input là 1 ký tự trắng, trả về emptystring với state = 1
• Khi input là chuỗi có 2 hay nhiều khoảng trắng mà không có ký tự khác
khoảng trắng, một emptystring trả về với state = 2
• Khi input là chuỗi chứa ký tự khác khoảng trắng ví dụ __ab___, chuỗi ab
được trả về với state = 3
Sau khi thực hiện, ta có bảng thực thi. Những bước đầu tiên kiểm tra phần đuôi của
chuỗi để xem có khoảng trắng không. Nếu có, chiều dài chuỗI được giảm đi cho đến khi
gặp được ký tự không là khoảng trắng đầu tiên và state được xác lập là 3
Bảng thực thi
Name Base Class State ( 0 – 3 )
Clear Space
Input empty : : ( return = empty ) ∧ State = 0
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 17
Đề tài: Môn Nguyên Lý Lập Trình 1
( Input ; State )
∨
Input = = 1 space : : ( return = empty ) ∧ State = 1
∨
Input > = 2 space : : ( return = empty ) ∧ State = 2
∨
Input có ký tự khác khoảng trắng : : ( return = Input (không có
khoảng trắng ở đầu và ở đuôi)) ∧ State =
• Các bảng lưu vết
Bảng lưu vết thường hiệu quả hơn bảng thực thi cho việc chứng thực tính
đúng đắn logic. Bảng lưu vết liên quan đến các kỹ thuật hỗ trợ cho nhau
1. Thực thi bằng biểu tượng
2. Kiểm tra bảng lưu vết của tất cả trường hợp có thể
3. Nguyên lý qui nạp
Kiểm tra logic bằng bảng lưu vết bằng cách phân định mọi trường hợp logic
có thể và kiểm tra từng trường hợp riêng lẻ. Đối vớI hàm Clear Space thực hiện
bằng cách định nghĩa một chuỗi Input có chiều dài k như sau
1. 0, 1, hoặc m character trong chuỗi với ký tự bắt đầu và kết thúc
không là khoảng trắng
2. 0, 1, hoặc j khoảng trắng đầu chuỗi
3. 0, 1, hoặc n khoảng trắng cuối chuỗi
Clear Space ( Input State )
1. Length = length ( Input )
State = 0
2. if length >0
3. Repeat until ( State = 3 or length = 0 )
4. if Input [ length-1 ] = ‘ ‘ { 1 ký tự trắng ở cuốI chuỗi Input
}
5. length = length – 1
6. if State < 2
State = State + 1
7. else State = 3
until ( State = 3 or length = 0 )
8. if length > 0
9. for N = 1 to length do
10. if ( Input [ N-1 ] < > ‘ ‘ or First )
11. Output = Output + Input [ N -1]
First = true
12. Input = Output
Ở đây j, m, n có thể là bất kỳ giá trị nguyên dương nào vớI k = j + m + n
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 18
Đề tài: Môn Nguyên Lý Lập Trình 1
Theo nguyên lý qui nạp, ta cần kiểm tra các trường hợp 0 và 1 sau đó kiểm chứng
logic với số q và q + 1 nào đó. Nếu bạn chứng tỏ logic đúng cho các trường hợp
này bạn có thể tin rằng logic là đúng.
Rõ ràng chương trình thao tác đúng đắn khi chuỗi rỗng. Nó để lại Length =
0 và State = 0
Cũng thế, bước 1 đến bước 11 bị bỏ qua khi Length = 0 và kết quả cuối cùng
Input = Output. Tuy nhiên điều này nảy sinh câu hỏi Output là gì khi chiều dài
Input = 0. Câu trả lời Output nên khởi động là chuỗi rỗng
Kế tiếp kiểm tra trường hợp không có khoảng trắng ở đuôi cũng như không
có khoảng trắng ở đầu và có tổng k Input ký tự. Rõ ràng trường hợp này đúng khi
k = 1. Kiểm tra các ký tự còn lại bằng phương pháp qui nạp, giả sử chương trình
đúng với k = q và xem có bất kỳ giá trị nào của n, nơi chương trình không đúng
với giá trị k = q + 1. Một kiểm tra logic cho thấy rằng không có trường hợp như
thế
Minh họa
1. Hình dung rằng logic đúng đối với giá trị k
0
nào đó. Điều này đúng
với k = 2, vì vậy k
0
= 2
2. Giả sử rằng logic là đúng với giá trị k nào đó lớn hơn k
0
3. Biết rằng logic đúng với k = q, chứng tỏ rằng nó vẫn đúng với k =
q + 1. Rõ ràng trường hợp q + 1 là thêm vào một vòng quay cho
bảng lưu vết trước và tạo ra Output thích hợp
Dầu kiểm chứng bằng lưu vết rất mất thới gian, nó không có nhiều phân định
cho tất cả các trường hợpkiểm tra logic của chương trình. Nếu bạn muốn phát triển
cuộc kiểm tra như trên thì bạn sẽ kiểm chứng tính đúng đắn cũa chương trình. Tuy
nhiên nếu bạn không dự định thực hiện, bạn phải kiểm tra cẩn thận trong suốt quá
trình thiết kế.
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 19
Đề tài: Môn Nguyên Lý Lập Trình 1
PHẦN II
THỰC NGHIỆM KIỂM CHỨNG CHƯƠNG TRÌNH
1. Trường hợp đơn giản
Bạn muốn biết đọan mã lệnh có thực hiện hay không ? Làm sao kiểm chứng điều
này? Có rất nhiều cách. Sử dụng kỹ thuật bẫy lỗi (debug) hay chạy từng bước để kiểm
tra kết quả từng đọan chương trình. kỹ thuật bẫy lỗi là ý tưởng khá hay nhưng lại
không tự động kiểm tra, bạn phải thực hiện mỗi khi có sự thay đổi. Kỹ thuật chạy
từng bước cũng tốt nhưng nó làm cho đọan mã lệnh không được dễ nhìn và hơn hết là
kết quả đưa ra có nhiều thông tin dư thừa.
Kiểm chứng bằng CppUnit có thể thưc hiện tự động, dễ cài đặt và bạn chỉ phải
viết một lần. Kiểm chứng bằng CppUnit giúp bạn thấy tự tin về chất lượng đọan mã
lệnh
Để thực hiện việc kiểm chứng, tất cả những gì bạn phải làm là tạo 1 lớp con
TestCase, được Override phương thức runTest(). Khi bạn muốn kiểm tra, gọi
CPPUNIT_ASSERT(bool) và truyền vào biểu thức cần kiểm tra. Nếu kết quả trả về
là True thì việc kiểm chứng thành công.
Đây là một ví dụ cho việc kiểm chứng so sánh hai số phức:
Class ComplexNumberTest: public CppUnit::TestCase {
Public:
ComplexNumberTeast (std::string name) : CppUnit::TestCase(name) {}
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 20
Đề tài: Môn Nguyên Lý Lập Trình 1
Void runTest() {
CPPUNIT_ASSERT (Complex (10,1) ==Complex(10,1));
CPPUNIT_ASSERT (!Complex (1,1) ==Complex(2,2));
}
};
Như bạn thấy, chương trình trên rất đơn giản. Tuy nhiên thường ta cần kiểm chứng
nhiều trường hợp nhỏ và ta muốn kiểm chứng trên cùng một tập các đối tượng. Để
thực hiện điều này ta sử dụng Fixture
2. Fixture
Fixture là một tập các đối tượng mà phục vụ cho lớp Test case cơ sở. Fixture sẽ
đảm bảo chắc chắn khi bạn kiểm chứng
Chúng ta sẽ khảo sát mẫu này. Giả sử chúng ta thực sự muốn phát triển một lớp
số phức. Chúng ta bắt đầu bằng việc định nghĩa lớp Complex rỗng như sau:
Class Complex ();
Bây giờ chúng ta sẽ tạo một thể hiện của lớp ComplexNumberTest ở trên, biên
dịch, chạy và xem kết quả. Trước tiên, chúng ta sẽ thấy lỗi biên dịch. Chương trình
test sử dụng tóan tử ==, nhưng chưa định nghĩa. Chúng ta sẽ sửa như sau:
Bool operato == (const Complex &a, const Complex &b)
{
Return true;
}
Bây giờ, biên dịch lại và chạy. Lúc này bạn sẽ không thấy lỗi biên dịch nữa.
Chúng ta cần thêm một chút vào operator == như sau:
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 21
Đề tài: Môn Nguyên Lý Lập Trình 1
Class Complex {
Friend bool operator == (const Complex &a, Const Complex &b
Double real, imaginary;
Public:
Complex (double r, double i=0): real (r), imaginary (i) {}
};
Bool operator== (const Complex &a, const Complex &b)
{
Return a.real== b.real && a.imaginary == b.imaginary;
}
Chúng ta biên dịch lại và việc kiểm chứng của chúng ta đã xong.
Tiếp theo chúng ta thêm một phép toán mới và kiểm chứng và ta làm như sau:
• Thêm biến cho mỗi phần của fixture
• Override setUp() để khởi tạo biến
• Override tearDown() để giải phóng bất cứ tài nguyên nào đang tồn tại
trong setUp()
Class ComplexNumberTest: public CppUnit::TestFixture{
Private:
Complex *m_10_1, *m_1_1, *m_11_2;
Public:
Void setUp()
{
m_10_1 = new Complex(10,1);
m_1_1= new Complex(1,1);
m_11_2 = new Complex (11,2);
}
void tearDown ()
{
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 22
Đề tài: Môn Nguyên Lý Lập Trình 1
delete m_10_1;
delete m_1_1;
delete m_11_2 ;
}
}
3. Test Case
Làm thế nào để gọi kiểm chứng sử dụng fixture? Có hai bước
• Viết testcase như là phương thức trong lớp fixture
• Tạo một TestCaller mà chạy như phương thức ngoại lệ
Đây là một ví dụ testcase:
Class ComplexNumberTest: public CppUnit::TestFixture {
Private:
Complex *m_10_1, *m_1_1, *m_11_2;
Public:
Void setUp()
{
m_10_1 = new Complex(10,1);
m_1_1= new Complex(1,1);
m_11_2 = new Complex (11,2);
}
void tearDown ()
{
delete m_10_1;
delete m_1_1;
delete m_11_2 ;
}
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 23
Đề tài: Môn Nguyên Lý Lập Trình 1
void testEquality ()
{
CPPUNIT_ASSERT (*m_10_1== *m_10_1);
CPPUNIT_ASSERT (!(*m_10_1)==*m_11_2);
}
void testAdditiion()
{
CPPUNIT_ASSERT (*m_10_1 + *m_1_1== *m_11_2);
}
};
Ta tạo và chạy những thể hiện cho mỗi testcase giống như sau:
CppUnit::TestCaller<ComplexNumber> tests (“testEquality”,
&ComplexNumberTest::testEquality);
CppUnit::TestResult result;
Test.run (&result);
Tham số thứ hai trong TestCaller là địa chỉ của phương thức
ComplexNumberTest. Khi chương trình chạy, phương thức đặc tả sẽ chạy. Điều này
thì không có ích lắm, tuy nhiên kết quả phân tích không hiển thị. Thường sử dụng
TestRunner để hiển thị kết quả
4. Suite
Làm sao cài đặt chương trình test của bạn để mà bạn có thể chạy chúng tất cả chỉ
một lần ? CppUnit cung cấp 1 lớp TestSuite mà chạy với bất kỳ số TestCases
Chúng ta đã khảo sát cách chạy một chương trình testcase đơn giản
Để tạo một bộ gồm 2 hay nhiều bộ test, ta làm như sau:
CppUnit::TestSuite suite;
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 24
Đề tài: Môn Nguyên Lý Lập Trình 1
CppUnit::TestResult result;
Suite.addTest (new CppUnit::TestCaller<ComplexNumberTest> (“testEquality”,
&ComplexNumberTest::testEquality));
Suite.addTest(new CppUnit::TestCaller<ComplexNumberTest>(“testAddition”,
&ComplexNumberTest::testAddition));
Suite.run (&result);
TestSuite không chỉ bao gồm lời gọi hàm TestCases.Chúng có thể chứa bất kỳ
đối tượng mà được cài đặt ở mức giao diện (interface) Test. Ví dụ, bạn có thể tạo một
TestSuite trong đoạn mã lệnh của bạn và tôi có thể tạo TestSuite trong đoạn mã lệnh
của tôi, và chúng ta có thể chạy cùng nhau bởi việc tạo một TestSuite chứa cả hai như
sau:
CppUnit::TestSuite suite;
CPPUnit::TestResult result;
Suite.addTest(ComplexNumberTest::suite());
Suite.addTest(SurrealNumberTest::suite());
Suite.run (&result);
5. TestRunner
Làm sao bạn chạy chương trình test của bạn và thu thập các kết quả đó? Mỗi khi
bạn có một test suite, bạn muốn chạy nó. CppUnit cung cấp công cụ cho phép bạn
định nghĩa suite để chạy và hiện thị kết quả. Bạn tạo một suite để truy cập vào
chương trình TestRunner với phương thức tĩnh làa suite để trả về một test suite.
Ví dụ, để tạo một suite ComplexNumber tới TestRunner, thêm đoạn code sau vào
ComplexNumberTest:
Public :
Static : CppUnit::Test *suite()
{
CppUnit::TestSuite *suiteOfTests = new CppUnit::TestSuite
(“ComplexNumberTest”);
Lê Hùng – Vũ Thế Hiển - Nguyễn Đình Hiển –Phạm Đào Minh Vũ – Trương Quang Bình Long
Trang 25