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à một chỉ số đo lường tỷ lệ phần trăm mã nguồn mà các bài kiểm thử của bạn thực thi. Tính năng này giúp bạn xác định những phần có thể chưa được kiểm thử đúng cách.

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

Tệp % tuyên bố % Branch 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 bạn thêm các tính năng và 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 tự tin hơn rằng ứng dụng của mình đã được kiểm thử kỹ lưỡng. Tuy nhiên, bạn vẫn có thể khám phá thêm nhiều tính năng khác.

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 mã: mức độ sử dụng hàm, dòng, nhánh và câu lệnh.

4 loại phạm vi áp dụng 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 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ử trên bản minh hoạ trực tiếp này hoặc xem kho lưu trữ.

Mức độ sử dụng 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. Chỉ số này ghi lại tỷ lệ phần trăm các hàm trong mã mà các chương trình kiểm thử của bạn gọi.

Trong ví dụ về mã, có hai hàm: calcCoffeeIngredientisValidCoffee. Các chương trình 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 mã đo lường tỷ lệ phần trăm số 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, thì tức 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à xanh lục) nhưng các bài kiểm thử không thực thi điều kiện americano (2 dòng) và hàm isValidCoffee (1 dòng). Điều này dẫn đến mức độ sử dụng dòng là 62,5%.

Xin lưu ý rằng mức độ sử dụng 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.

Mức độ bao phủ của 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 độ sử dụng nhánh đo lường tỷ lệ phần trăm các nhánh hoặc điểm quyết định được thực thi trong mã, chẳng hạn như câu lệnh if hoặc vòng lặp. Hàm này xác định xem các chương trình 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 kiểm.
  2. Gọi calcCoffeeIngredient bằng coffeeNamecup Dấu chek.
  3. Cà phê là Espresso Dấu kiểm.
  4. Cà phê kiểu Mỹ Dấu X.
  5. Dấu kiểm. cà phê khác

Kiểm thử bao gồm tất cả các nhánh, ngoại trừ điều kiện Coffee is Americano. Do đó, mức độ bao phủ 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âu lệnh đo lường tỷ lệ phần trăm câu lệnh trong mã mà các bài kiểm thử của bạn thực thi. Khi mới nhìn thoáng qua, bạn có thể thắc mắc "chẳng phải điều này giống với mức độ bao phủ của dòng sao?" Trên thực tế, mức độ bao phủ của câu lệnh cũng tương tự như mức độ sử dụng dòng nhưng có tính đến một dòng mã chứa nhiều câu lệnh.

Trong ví dụ về mã, có 8 dòng mã có thể thực thi nhưng có 9 câu lệnh. Bạn có thể phát hiện 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 độ sử dụng dòng sẽ tương tự như mức độ sử dụng 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ụ đo lường mức độ sử dụng mã đều bao gồm 4 loại mức độ sử dụng mã phổ biến sau. Việc chọn chỉ số mức độ sử dụng mã cần ưu tiên 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, mức độ sử dụng câu lệnh là một đ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 đó, các câu lệnh này là một tiến trình tự nhiên sau phạm vi câu lệnh.

Sau khi đạt được mức độ phù hợp cao với câu lệnh, bạn có thể chuyển sang mức độ phù hợp với nhánh và mức độ phù hợp với hàm.

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

Không. Mức độ kiểm thử và mức độ sử dụng mã thường bị nhầm lẫn nhưng chúng khác nhau:

  • Mức độ bao phủ kiểm thử: Một chỉ số định tính đo lường mức độ phù hợp của bộ kiểm thử với các tính năng của phần mềm. Điều này giúp xác định mức độ rủi ro liên quan.
  • Mức độ sử dụng mã: Một chỉ số định lượng đo lường tỷ lệ phần trăm mã được thực thi trong quá trình kiểm thử. Đây là về 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 là một ngôi nhà.

  • Mức độ bao phủ kiểm thử đo lường mức độ hiệu quả của các bài kiểm thử trong việc bao phủ các phòng trong nhà.
  • Mức độ sử dụng mã đo lường mức độ kiểm thử đã thực hiện.

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

Mặc dù bạn nê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 rằng mã của bạn không có lỗi hoặc khiếm khuyết.

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

Hãy xem xét 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 100% mức độ bao phủ hàm, dòng, nhánh và câu lệnh, nhưng điều này không có ý nghĩa vì kiểm thử này 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ố

Một chỉ số không chính xác có thể khiến bạn có cảm giác an toàn giả tạo, điều này còn tệ hơn là không có chỉ số nào cả. Ví dụ: nếu có một bộ kiểm thử đạt được mức độ sử dụng mã là 100% nhưng tất cả các bài kiểm thử đều vô nghĩa, thì bạn có thể có cảm giác an toàn sai lầm rằng mã của mình đã được kiểm thử kỹ lưỡng. Nếu bạn vô tình xoá hoặc làm hỏng một phần mã ứng dụng, thì các bài kiểm thử vẫn sẽ vượt qua, mặc dù ứng dụng không còn hoạt động đúng cách.

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

  • Xem xét kiểm tra. Viết và xem xét 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 khác nhau.
  • Sử dụng mức độ sử dụng mã làm nguyên tắc, chứ không phải là thước đo duy nhất về hiệu quả kiểm thử hoặc chất lượng mã.

Sử dụng mức độ sử dụng mã trong các 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ã với 3 loại 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ì đượ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 sẽ tính mức độ bao phủ của một phần lớn hơn trong mã nguồn và có thể khó xác định bài kiểm thử nào thực sự bao phủ 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ã, bạn nên sử dụng mức độ sử dụng yêu cầu. Lý do là trọng tâm của kiểm thử E2E là đáp ứng các yêu cầu của kiểm thử, chứ không phải tập trung vào mã nguồn.

Kết luận

Mức độ sử dụng mã có thể là một chỉ số hữu ích để đo lường mức độ hiệu quả của các bài kiểm thử. Điều này có thể giúp bạn cải thiện chất lượng ứ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ử kỹ lưỡng.

Tuy nhiên, hãy nhớ rằng mức độ sử dụng mã chỉ là một chỉ số. Hãy nhớ cân nhắc các yếu tố khác, chẳng hạn như chất lượng của kiểm thử và yêu cầu của ứng dụng.

Mục tiêu không phải là đạt được mức độ sử dụng mã là 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 thức 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({});
  });
});