Độ phức tạp Cyclomatic trong kiểm thử phần mềm kèm ví dụ

⚡ Tóm tắt thông minh

Độ phức tạp chu trình (Cyclomatic Complexity) là một chỉ số phần mềm được Thomas McCabe phát triển vào năm 1976, dùng để đếm số lượng các đường dẫn độc lập trong một chương trình. Nó được tính toán từ đồ thị luồng điều khiển và cho biết số lượng trường hợp kiểm thử cần thiết để đạt được độ phủ nhánh đầy đủ.

  • 📐 Hai công thức: V(G) = E – N + 2 từ đồ thị, hoặc V(G) = P + 1 từ số điểm quyết định.
  • 🧮 Ý nghĩa trực tiếp: Giá trị này bằng số lượng đường dẫn độc lập tối đa, và do đó là số lượng trường hợp thử nghiệm cần thiết.
  • 4 Cơ sở đồ thị: Các nút đại diện cho các bước xử lý và các cạnh đại diện cho luồng điều khiển giữa chúng.
  • 🟢 1 đến 10: Mã nguồn được cấu trúc tốt, viết mạch lạc, dễ kiểm thử và chi phí bảo trì thấp.
  • 🟠 21 đến 40: Mã nguồn rất phức tạp và khó kiểm thử, trong đó việc tái cấu trúc thường tốn ít chi phí hơn so với việc kiểm thử.
  • 🛠️ dụng cụ: SonarQube, Visual Studio Code Metrics, Radon và Lizard tự động tính toán điều đó.

Độ phức tạp theo chu kỳ trong kiểm thử phần mềm

Độ phức tạp Cyclomatic của McCabe là gì?

Độ phức tạp theo chu kỳ trong kiểm thử phần mềm là một phép đo kiểm tra được sử dụng để đo độ phức tạp của một chương trình phần mềm. Đây là phép đo định lượng các đường dẫn độc lập trong mã nguồn của một chương trình phần mềm. Độ phức tạp theo chu kỳ có thể được tính bằng cách sử dụng biểu đồ luồng điều khiển hoặc liên quan đến các hàm, mô-đun, phương pháp hoặc lớp trong một chương trình phần mềm.

Đường đi độc lập được định nghĩa là đường đi có ít nhất một cạnh chưa được đi qua trước đó trong bất kỳ đường đi nào khác.

Số liệu này được Thomas J. McCabe phát triển vào năm 1976 và nó dựa trên cách trình bày luồng điều khiển của chương trình. Luồng điều khiển mô tả chương trình dưới dạng biểu đồ bao gồm Nút và Cạnh.

Trong biểu đồ, các Nút biểu thị các tác vụ xử lý trong khi các cạnh biểu thị luồng điều khiển giữa các nút.

Độ phức tạp Cyclomatic của McCabe

Ký hiệu đồ thị luồng cho một chương trình

Ký hiệu Biểu đồ luồng cho một chương trình xác định một số nút được kết nối qua các cạnh. Dưới đây là sơ đồ luồng cho các câu lệnh như if-else, While, cho đến khi và trình tự luồng thông thường.

Ký hiệu biểu đồ luồng cho một chương trình

Cách tính độ phức tạp Cyclomatic

Biểu diễn toán học:

Về mặt toán học, đó là tập hợp các đường đi độc lập xuyên qua sơ đồ đồ thị. Code Độ phức tạp của chương trình có thể được xác định bằng công thức –

V(G) = E - N + 2

Ở đâu,

E – Số cạnh

N – Số lượng nút

V (G) = P + 1

Trong đó P = Số nút vị ngữ (nút chứa điều kiện)

Thí dụ -

i = 0;
n=4; //N-Number of nodes present in the graph

while (i<n-1) do
j = i + 1;

while (j<n) do

if A[i]<A[j] then
swap(A[i], A[j]);

end do;
j=j+1;

end do;

Biểu đồ luồng cho chương trình này sẽ là

Tính toán độ phức tạp Cyclomatic

Tính toán theo phương pháp toán học,

  • V(G) = 9 – 7 + 2 = 4
  • V(G) = 3 + 1 = 4 (Nút điều kiện là 1,2 và 3 nút)

Bộ cơ sở, bốn con đường thực thi độc lập:

  • 1, 7
  • 1, 2, 6, 1, 7
  • 1, 2, 3, 4, 5, 2, 6, 1, 7
  • 1, 2, 3, 5, 2, 6, 1, 7

Tính chất của độ phức tạp chu trình

Sau đây là các tính chất của độ phức tạp Cyclomatic:

  1. V(G) là số đường đi độc lập tối đa trong đồ thị
  2. V(G) >=1
  3. G sẽ có một đường đi nếu V(G) = 1
  4. Một nguyên tắc thường được sử dụng là giữ V(G) ở mức 10 hoặc thấp hơn cho một mô-đun đơn lẻ.

Chỉ số này hữu ích như thế nào đối với việc kiểm thử phần mềm?

Kiểm thử đường dẫn cơ sở là một trong những kỹ thuật kiểm thử hộp trắng và nó đảm bảo thực thi ít nhất một câu lệnh trong quá trình kiểm thử. Nó kiểm tra từng đường dẫn độc lập tuyến tính trong chương trình, có nghĩa là... Số lượng trường hợp kiểm thử cần thiết bằng độ phức tạp chu trình của chương trình..

Đo lường này hữu ích vì các đặc tính của Độ phức tạp Cyclomatic (M) –

  1. M có thể là số ca kiểm thử để đạt được phạm vi bao phủ nhánh (Giới hạn trên)
  2. M có thể là số đường đi qua đồ thị. (Chặn dưới)

Hãy xem xét ví dụ này –

If (Condition 1)
Statement 1

Else
Statement 2

If (Condition 2)
Statement 3

Else
Statement 4

Độ phức tạp theo chu kỳ cho chương trình này sẽ là 8-7+2=3.

Vì độ phức tạp được tính là 3, nên cần có ba trường hợp thử nghiệm để có thể bao phủ toàn bộ đường dẫn cho ví dụ trên.

Các bước cần tuân theo

Cần thực hiện theo các bước sau để tính toán độ phức tạp Cyclomatic và thiết kế trường hợp thử nghiệm.

Bước 1 – Xây dựng đồ thị với các nút và cạnh từ mã

Bước 2 – Xác định các đường dẫn độc lập

Bước 3 – Tính toán độ phức tạp Cyclomatic

Bước 4 – Thiết kế các trường hợp thử nghiệm

Khi tập hợp cơ bản được hình thành, CÁC TRƯỜNG HỢP KIỂM TRA nên được viết để thực hiện tất cả các đường dẫn.

Thông tin thêm về V (G)

Độ phức tạp của chu kỳ có thể được tính toán thủ công nếu chương trình nhỏ. Các công cụ tự động cần được sử dụng nếu chương trình rất phức tạp vì điều này liên quan đến nhiều biểu đồ luồng hơn. Dựa trên số độ phức tạp, nhóm có thể kết luận về các hành động cần thực hiện để đo lường.

Bảng sau đây cung cấp tổng quan về số phức tạp và ý nghĩa tương ứng của v (G):

Số phức tạp Ý nghĩa
1 để 10

Mã nguồn được cấu trúc tốt và viết mạch lạc.

Khả năng kiểm tra cao

Chi phí và công sức ít hơn

11 để 20

Mã phức tạp

Khả năng kiểm thử trung bình

Chi phí và công sức ở mức trung bình

21 để 40

Mã rất phức tạp

Khả năng kiểm thử thấp

Chi phí và công sức bỏ ra rất cao.

> 40

Hoàn toàn không thể kiểm chứng được

Chi phí và công sức rất cao

Công cụ tính toán độ phức tạp chu trình

Có nhiều công cụ để xác định độ phức tạp của ứng dụng. Một số công cụ tính toán độ phức tạp được sử dụng cho các công nghệ cụ thể. Độ phức tạp có thể được tìm thấy bằng số điểm quyết định trong một chương trình. Các điểm quyết định là if, for, for-each, while, do, catch, case trong mã nguồn.

Ví dụ về các công cụ là

  • OCLint – Trình phân tích mã tĩnh cho C và các ngôn ngữ liên quan
  • SonarQube – Báo cáo độ phức tạp chu trình và nhận thức trên hơn 25 ngôn ngữ
  • Visual Studio Code Số liệu – Phân tích độ phức tạp chu trình tích hợp sẵn cho các assembly .NET
  • Radon và Lizard – Các công cụ phân tích độ phức tạp dòng lệnh dành cho Python và cho các dự án đa ngôn ngữ tương ứng
  • GMetrics – Tìm số liệu trong Java ứng dụng liên quan

Ứng dụng của độ phức tạp chu trình

Độ phức tạp của chu kỳ có thể chứng minh là rất hữu ích trong

  • Giúp các nhà phát triển và người thử nghiệm xác định việc thực thi đường dẫn độc lập
  • Các nhà phát triển có thể đảm bảo rằng tất cả các đường dẫn đã được kiểm tra ít nhất một lần.
  • Giúp chúng ta tập trung hơn vào những con đường chưa được khám phá
  • Cải thiện phạm vi bao phủ mã trong Kỹ thuật phần mềm
  • Đánh giá rủi ro liên quan đến ứng dụng hoặc chương trình
  • Sử dụng các số liệu này sớm trong chu kỳ sẽ giảm bớt rủi ro cho chương trình

Cách giảm độ phức tạp của chu trình

Chỉ số độ phức tạp cao là một tín hiệu, chứ không phải là một phán quyết. Bốn lần tái cấu trúc chiếm phần lớn mức giảm độ phức tạp có thể đạt được trong thực tế.

  • Extracphương pháp t. Việc tách một nhánh thành một hàm riêng biệt sẽ phân chia độ phức tạp giữa hai mô-đun. Tổng độ phức tạp của toàn hệ thống không thay đổi, nhưng mỗi đơn vị trở nên có thể kiểm thử độc lập.
  • Thay thế chuỗi điều kiện bằng một câu lệnh tra cứu. Một chuỗi câu lệnh if-else-if dài kiểm tra cùng một biến sẽ trở thành một cấu trúc map hoặc switch, giúp thu gọn nhiều điểm quyết định thành một.
  • Hãy sử dụng các điều khoản bảo vệ. Việc trả về sớm khi nhận được đầu vào không hợp lệ sẽ loại bỏ sự lồng ghép mà một khối if-else lớn duy nhất tạo ra, mà không làm thay đổi hành vi.
  • Thay thế các câu lệnh điều kiện bằng đa hình. Trong trường hợp câu lệnh điều kiện chuyển đổi dựa trên kiểu dữ liệu, việc tách mỗi nhánh thành một lớp riêng biệt sẽ loại bỏ hoàn toàn quyết định đó.

Trước đây, với V(G) = 4:

if (user != null) {
    if (user.isActive()) {
        if (user.hasRole("admin")) {
            return grantAccess();
        }
    }
}
return denyAccess();

Sau đó, với cùng hành vi đó nhưng đã loại bỏ cấu trúc lồng nhau:

if (user == null) return denyAccess();
if (!user.isActive()) return denyAccess();
if (!user.hasRole("admin")) return denyAccess();
return grantAccess();

Một lưu ý về chỉ số này. Độ phức tạp chu trình đếm số quyết định, chứ không phải độ khó. Một câu lệnh switch với hai mươi trường hợp đơn giản đạt điểm 21 nhưng vẫn dễ đọc, trong khi một khối lồng nhau sâu đạt điểm 8 có thể khó hiểu hơn nhiều. Hãy sử dụng con số này để tìm ra các ứng viên cần xem xét lại, chứ không phải là mục tiêu để gian lận.

Câu Hỏi Thường Gặp

Thông thường, mỗi module có số lượng code dưới 10. Từ 11 đến 20, mã nguồn phức tạp nhưng vẫn có thể quản lý được. Trên 20 code, khả năng kiểm thử giảm mạnh, và trên 40 code, module thường được coi là không thể kiểm thử được như hiện tại.

Cả hai đều cho cùng một kết quả. P + 1 nhanh hơn khi tính toán thủ công vì bạn chỉ cần đếm các điểm quyết định. E – N + 2 là phương pháp mà các công cụ sử dụng, vì chúng đã xây dựng sẵn đồ thị luồng điều khiển.

Không nhất thiết. Độ phức tạp chu trình tính toán số lượng quyết định chứ không phải độ khó, vì vậy một câu lệnh switch đơn giản với hai mươi trường hợp dễ hiểu sẽ đạt điểm cao. Hãy coi con số này như một lời nhắc nhở để xem xét lại.

Họ kết hợp điều này với tần suất thay đổi và lịch sử lỗi để xếp hạng các mô-đun nào có rủi ro cao nhất, hướng nỗ lực xem xét và kiểm thử vào đoạn mã có khả năng gây lỗi cao nhất.

Đúng vậy. Trợ lý AI đề xuất các điều khoản bảo vệ, ví dụ:tracCác phương pháp được sửa đổi và bảng tra cứu giúp giảm số lượng. Xác minh hành vi bằng bộ kiểm thử hiện có, vì việc tái cấu trúc làm thay đổi logic sẽ làm mất đi mục đích ban đầu.

Tóm tắt bài viết này với: