4 loại phạm vi phổ biến của mã

Tìm hiểu về mức độ sử dụng mã và khám phá 4 cách phổ biến để đo lường mức độ sử dụng mã.

Bạn đã nghe thấy cụm từ "mức độ sử dụng mã" chưa? Trong bài đăng này, chúng ta sẽ tìm hiểu về mức độ sử dụng mã trong kiểm thử và 4 cách phổ biến để đo lường mức độ sử dụng mã.

Mức độ sử dụng mã là gì?

Mức độ sử dụng mã là chỉ số đo lường tỷ lệ phần trăm mã nguồn mà hoạt động kiểm thử của bạn thực thi. Công cụ này giúp bạn xác định những khía cạnh có thể chưa có quy trình kiểm thử phù hợp.

Thông thường, việc ghi lại các chỉ số này trông giống như sau:

Tệp % tuyên bố Nhánh% Hàm% % dòng Các tuyến giao thông công cộng đã được phát hiện
file.js 90% 100% 90% 80% 89.256
coffee.js 55,55% 80% 50% 62,5% 10 – 11, 18

Khi thêm các tính năng và hoạt động kiểm thử mới, việc tăng tỷ lệ phần trăm mức độ sử dụng mã có thể giúp bạn yên tâm hơn rằng ứng dụng đã được kiểm thử kỹ lưỡng. Tuy nhiên, vẫn còn nhiều điều khác để khám phá.

4 loại phạm vi áp dụng mã phổ biến

Có 4 cách phổ biến để thu thập và tính toán mức độ sử dụng của mã: mức độ sử dụng hàm, dòng, nhánh và câu lệnh.

4 loại độ bao phủ văn bản.

Để xem cách tính tỷ lệ phần trăm của từng loại mã, hãy xem xét mã ví dụ sau đây để tính toán thành phần cà phê:

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  return {};
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}

Các bài kiểm thử xác minh hàm calcCoffeeIngredient là:

/* coffee.test.js */

import { describe, expect, assert, it } from 'vitest';
import { calcCoffeeIngredient } from '../src/coffee-incomplete';

describe('Coffee', () => {
  it('should have espresso', () => {
    const result = calcCoffeeIngredient('espresso', 2);
    expect(result).to.deep.equal({ espresso: 60 });
  });

  it('should have nothing', () => {
    const result = calcCoffeeIngredient('unknown');
    expect(result).to.deep.equal({});
  });
});

Bạn có thể chạy mã và kiểm thử trong bản minh hoạ trực tiếp này hoặc xem kho lưu trữ.

Phạm vi của hàm

Mức độ sử dụng mã: 50%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  // ...
}

function isValidCoffee(name) {
  // ...
}

Mức độ sử dụng hàm là một chỉ số đơn giản. Phần này ghi lại tỷ lệ phần trăm hàm trong mã mà chương trình kiểm thử gọi.

Trong ví dụ về mã, có 2 hàm: calcCoffeeIngredientisValidCoffee. Các bài kiểm thử chỉ gọi hàm calcCoffeeIngredient, vì vậy, mức độ sử dụng hàm là 50%.

Mức độ che phủ đường kẻ

Mức độ sử dụng mã: 62,5%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  return {};
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}

Mức độ sử dụng dòng đo lường tỷ lệ phần trăm dòng mã có thể thực thi mà bộ kiểm thử của bạn đã thực thi. Nếu một dòng mã vẫn chưa được thực thi, điều đó có nghĩa là một số phần của mã chưa được kiểm thử.

Ví dụ về mã có 8 dòng mã có thể thực thi (được làm nổi bật bằng màu đỏ và màu xanh lục) nhưng các chương trình kiểm thử không thực thi điều kiện americano (2 dòng) và hàm isValidCoffee (một dòng). Nhờ đó, tỷ lệ bao phủ của đường là 62,5%.

Xin lưu ý rằng mức độ bao phủ của dòng không tính đến các câu lệnh khai báo (chẳng hạn như function isValidCoffee(name)let espresso, water;) vì các câu lệnh này không thể thực thi.

Phạm vi chi nhánh

Mức độ sử dụng mã: 80%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  // ...

  if (coffeeName === 'espresso') {
    // ...
    return { espresso };
  }

  if (coffeeName === 'americano') {
    // ...
    return { espresso, water };
  }

  return {};
}

Mức độ bao phủ của nhánh đo lường tỷ lệ phần trăm các nhánh được thực thi hoặc điểm quyết định trong mã, chẳng hạn như câu lệnh if hoặc vòng lặp. Quy tắc này xác định liệu các bài kiểm thử có kiểm tra cả nhánh đúng và sai của câu lệnh có điều kiện hay không.

Có 5 nhánh trong ví dụ về mã:

  1. Gọi calcCoffeeIngredient chỉ bằng coffeeName Dấu chek.
  2. Đang gọi calcCoffeeIngredient bằng coffeeNamecup Dấu chek.
  3. Cà phê Espresso Dấu chek.
  4. Cà phê kiểu Mỹ Dấu X.
  5. Cà phê khác Dấu chek.

Kiểm thử bao gồm tất cả các nhánh, ngoại trừ điều kiện Coffee is Americano. Vì vậy, tỷ lệ bao phủ của chi nhánh là 80%.

Phạm vi bao phủ của bảng sao kê

Mức độ sử dụng mã: 55,55%

/* coffee.js */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  return {};
}

export function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}

Mức độ sử dụng của câu lệnh đo lường tỷ lệ phần trăm câu lệnh trong mã mà chương trình kiểm thử thực thi. Khi mới nhìn thoáng qua, bạn có thể thắc mắc "chẳng phải công cụ này cũng giống như độ phủ đường hay không?" Trên thực tế, mức độ sử dụng câu lệnh tương tự như mức độ sử dụng dòng nhưng có tính đến các dòng mã đơn chứa nhiều câu lệnh.

Trong ví dụ về mã, có 8 dòng mã thực thi nhưng có 9 câu lệnh. Bạn có thể tìm ra dòng chứa hai câu lệnh không?

Kiểm tra câu trả lời của bạn

Đó là dòng sau: espresso = 30 * cup; water = 70 * cup;

Các thử nghiệm chỉ bao gồm năm trong số chín câu lệnh, do đó, mức độ bao phủ của câu lệnh là 55,55%.

Nếu bạn luôn viết một câu lệnh trên mỗi dòng thì mức độ bao phủ của dòng sẽ tương tự như mức độ bao phủ của câu lệnh.

Bạn nên chọn loại mức độ sử dụng mã nào?

Hầu hết các công cụ mức độ sử dụng mã đều có bốn loại mức độ sử dụng mã phổ biến này. Việc ưu tiên chỉ số mức độ sử dụng mã phụ thuộc vào các yêu cầu cụ thể của dự án, phương pháp phát triển và mục tiêu kiểm thử.

Nhìn chung, phạm vi của các câu lệnh là điểm khởi đầu phù hợp vì đây là một chỉ số đơn giản và dễ hiểu. Không giống như phạm vi của câu lệnh, phạm vi bao phủ của nhánh và phạm vi bao phủ của hàm đo lường xem các chương trình kiểm thử gọi một điều kiện (nhánh) hay một hàm. Do đó, chúng là một tiến trình tự nhiên sau phạm vi của câu lệnh.

Sau khi đạt được mức độ sử dụng câu lệnh cao, bạn có thể chuyển sang mức độ sử dụng nhánh và mức độ sử dụng hàm.

Mức độ sử dụng kiểm thử có giống như mức độ sử dụng mã không?

Không. Mức độ sử dụng mã và phạm vi kiểm thử thường bị nhầm lẫn, nhưng chúng có hai điểm khác biệt:

  • Phạm vi kiểm thử: Chỉ số đầy đủ đo lường mức độ phù hợp của bộ kiểm thử đối với các tính năng của phần mềm. Dữ liệu này giúp xác định mức độ rủi ro liên quan.
  • Mức độ sử dụng mã: Chỉ số định lượng đo lường tỷ lệ mã được thực thi trong quá trình kiểm thử. Nội dung liên quan đến lượng mã mà các bài kiểm thử bao gồm.

Dưới đây là một ví dụ đơn giản: hãy tưởng tượng một ứng dụng web như một ngôi nhà.

  • Phạm vi kiểm thử đo lường mức độ hiệu quả của các bài kiểm tra trong các phòng trong nhà.
  • Mức độ sử dụng mã đo lường số lượng nhà mà các bài kiểm thử đã trải qua.

Mức độ sử dụng mã 100% không có nghĩa là không có lỗi

Mặc dù bạn luôn mong muốn đạt được mức độ sử dụng mã cao trong quá trình kiểm thử, nhưng mức độ sử dụng mã 100% không đảm bảo sẽ không có lỗi hoặc khiếm khuyết trong mã của bạn.

Một cách vô nghĩa để đạt được mức độ sử dụng mã 100%

Hãy cân nhắc kiểm thử sau:

/* coffee.test.js */

// ...
describe('Warning: Do not do this', () => {
  it('is meaningless', () => { 
    calcCoffeeIngredient('espresso', 2);
    calcCoffeeIngredient('americano');
    calcCoffeeIngredient('unknown');
    isValidCoffee('mocha');
    expect(true).toBe(true); // not meaningful assertion
  });
});

Kiểm thử này đạt được mức độ sử dụng hàm, dòng, nhánh và câu lệnh 100%, nhưng không hợp lý vì nó không thực sự kiểm thử mã. Lời xác nhận expect(true).toBe(true) sẽ luôn truyền bất kể mã có hoạt động chính xác hay không.

Chỉ số không tốt còn tệ hơn là không có chỉ số

Chỉ số không hợp lệ có thể khiến bạn lầm tưởng về sự an toàn, còn tệ hơn cả việc không có chỉ số. Ví dụ: nếu bạn có một bộ kiểm thử đạt được mức độ sử dụng mã 100% nhưng các bài kiểm thử đều vô nghĩa, thì có thể bạn sẽ lầm tưởng rằng mã của mình đã được kiểm thử tốt. Nếu bạn vô tình xoá hoặc làm hỏng một phần của mã xử lý ứng dụng, các bài kiểm thử vẫn sẽ thành công, ngay cả khi ứng dụng không còn hoạt động chính xác nữa.

Cách tránh tình huống này:

  • Xem xét kiểm tra. Viết và xem lại các bài kiểm thử để đảm bảo chúng có ý nghĩa và kiểm thử mã trong nhiều tình huống.
  • Sử dụng mức độ sử dụng mã làm nguyên tắc, không phải là thước đo duy nhất để đo lường hiệu quả của hoạt động kiểm thử hoặc chất lượng mã.

Sử dụng mức độ sử dụng mã trong nhiều loại kiểm thử

Hãy cùng tìm hiểu kỹ hơn về cách bạn có thể sử dụng mức độ sử dụng mã bằng 3 loại hình kiểm thử phổ biến:

  • Kiểm thử đơn vị. Đây là loại kiểm thử tốt nhất để thu thập mức độ sử dụng mã vì chúng được thiết kế để bao gồm nhiều tình huống nhỏ và đường dẫn kiểm thử.
  • Kiểm thử tích hợp. Các thư viện này có thể giúp thu thập mức độ sử dụng mã cho việc kiểm thử tích hợp, nhưng hãy thận trọng khi sử dụng. Trong trường hợp này, bạn tính toán mức độ sử dụng của một phần lớn mã nguồn và có thể khó xác định chương trình kiểm thử nào thực sự bao hàm phần nào của mã. Tuy nhiên, việc tính toán mức độ sử dụng mã của các bài kiểm thử tích hợp có thể hữu ích đối với các hệ thống cũ không có các đơn vị được tách biệt tốt.
  • Kiểm thử toàn diện (E2E). Việc đo lường mức độ sử dụng mã cho các bài kiểm thử E2E gặp khó khăn và khó khăn do tính chất phức tạp của các bài kiểm thử này. Thay vì sử dụng mức độ sử dụng mã, mức độ sử dụng yêu cầu có thể là cách tốt hơn. Lý do là kiểm thử E2E tập trung vào các yêu cầu của kiểm thử chứ không phải là tập trung vào mã nguồn.

Kết luận

Mức độ sử dụng mã có thể là chỉ số hữu ích để đo lường mức độ hiệu quả của kiểm thử. Cách này có thể giúp bạn cải thiện chất lượng của ứng dụng bằng cách đảm bảo rằng logic quan trọng trong mã của bạn được kiểm thử tốt.

Tuy nhiên, hãy nhớ rằng mức độ sử dụng mã chỉ là một chỉ số. Đừng quên cân nhắc các yếu tố khác, chẳng hạn như chất lượng của bài kiểm tra và yêu cầu đối với đơn đăng ký.

Bạn không nên nhắm đến việc đạt được mức độ sử dụng mã 100%. Thay vào đó, bạn nên sử dụng mức độ sử dụng mã cùng với một kế hoạch kiểm thử toàn diện kết hợp nhiều phương pháp kiểm thử, bao gồm kiểm thử đơn vị, kiểm thử tích hợp, kiểm thử toàn diện và kiểm thử thủ công.

Xem ví dụ về mã đầy đủ và các bài kiểm thử với mức độ sử dụng mã tốt. Bạn cũng có thể chạy mã và kiểm thử bằng bản minh hoạ trực tiếp này.

/* coffee.js - a complete example */

export function calcCoffeeIngredient(coffeeName, cup = 1) {
  if (!isValidCoffee(coffeeName)) return {};

  let espresso, water;

  if (coffeeName === 'espresso') {
    espresso = 30 * cup;
    return { espresso };
  }

  if (coffeeName === 'americano') {
    espresso = 30 * cup; water = 70 * cup;
    return { espresso, water };
  }

  throw new Error (`${coffeeName} not found`);
}

function isValidCoffee(name) {
  return ['espresso', 'americano', 'mocha'].includes(name);
}
/* coffee.test.js - a complete test suite */

import { describe, expect, it } from 'vitest';
import { calcCoffeeIngredient } from '../src/coffee-complete';

describe('Coffee', () => {
  it('should have espresso', () => {
    const result = calcCoffeeIngredient('espresso', 2);
    expect(result).to.deep.equal({ espresso: 60 });
  });

  it('should have americano', () => {
    const result = calcCoffeeIngredient('americano');
    expect(result.espresso).to.equal(30);
    expect(result.water).to.equal(70);
  });

  it('should throw error', () => {
    const func = () => calcCoffeeIngredient('mocha');
    expect(func).toThrowError(new Error('mocha not found'));
  });

  it('should have nothing', () => {
    const result = calcCoffeeIngredient('unknown')
    expect(result).to.deep.equal({});
  });
});