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

Tìm hiểu về mức độ phù hợp của mã và khám phá 4 cách phổ biến để đo lường mức độ phù hợp.

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à các bài kiểm thử của bạn thực thi. Dữ liệu này giúp bạn xác định những khía cạnh có thể chưa được kiểm tra đúng cách.

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

Tệp Câu lệnh% % nhánh Hàm% % dòng Đường kẻ bị lộ
file.js 90% 100% 90% Tăng 80% 89.256
coffee.js 55,55% Tăng 80% 50% 62,5% 10-11, 18

Khi thêm các tính năng và bài 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ủa mình đã được kiểm thử kỹ lưỡng. Tuy nhiên, vẫn còn nhiều điều khác để bạn khám phá.

4 loại mức độ sử dụng mã phổ biến

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

Bốn kiểu bao phủ văn bản.

Để xem cách từng loại mức độ sử dụng mã tính tỷ lệ phần trăm, hãy xem xét mã ví dụ sau đây để tính 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 chương trình 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à thử nghiệm trong bản minh hoạ trực tiếp này hoặc xem kho lưu trữ.

Phạm vi bao phủ của hàm

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

/* coffee.js */

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

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

Mức độ phù hợp của hàm là một chỉ số đơn giản. Hàm này ghi lại tỷ lệ phần trăm hàm trong mã mà bài kiểm thử của bạn gọi.

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

Mức độ phù hợp của đườ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 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, đ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 kiểm thử không thực thi điều kiện americano (2 dòng) và hàm isValidCoffee (một dòng). Kết quả là mức độ phù hợp của tuyến 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.

Phạm vi chi nhánh

Mức độ bao phủ của 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. Mô hình tổ hợp tiếp thị xác định xem các 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 cho calcCoffeeIngredient chỉ bằng coffeeName Dấu kiểm.
  2. Gọi calcCoffeeIngredient với coffeeNamecup Dấu kiểm.
  3. Cà phê là Espresso Dấu kiểm.
  4. Cà phê là Americano Dấu X.
  5. Cà phê khác Dấu kiểm.

Các bài 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 báo cáo

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 độ bao phủ của câu lệnh đo lường tỷ lệ phần trăm số câu lệnh trong mã mà 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: "mức độ sử dụng câu lệnh này có giống như mức độ sử dụng dòng không?" Thực tế, mức độ sử dụng câu lệnh cũng tương tự như mức độ sử dụng dòng nhưng tính đến các dòng mã riêng lẻ 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 thấy dòng chứa hai tuyên bố không?

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

Dòng sau: espresso = 30 * cup; water = 70 * cup;

Kiểm thử chỉ bao gồm 5 trong số 9 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 mỗi dòng, mức độ bao phủ dòng của bạn sẽ tương tự như phạm vi câu lệnh của bạn.

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ụ về 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ố về mức độ sử dụng mã cần ưu tiên sẽ 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ử.

Nói chung, mức độ phù hợp của câu lệnh là xuất phát điểm lý tưởng vì đây là chỉ số đơn giản và dễ hiểu. Không giống như mức độ phù hợp của câu lệnh, mức độ bao phủ của nhánh và mức độ sử dụng hàm đo lường xem kiểm thử gọi một điều kiện (nhánh) hay một hàm. Do đó, chúng là tiến trình tự nhiên sau mức độ phù hợp 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 mã của hoạt động kiểm thử có giống như mức độ sử dụng mã không?

Không. Phạm vi kiểm thử và mức độ bao phủ của mã thường bị nhầm lẫn nhưng chúng khác nhau:

  • Phạm vi kiểm thử: Chỉ số mang tính chất lượng để đo lường mức độ hiệu quả của bộ kiểm thử đối với các tính năng của phần mềm. Giúp xác định mức độ rủi ro có liên quan.
  • Mức độ sử dụng mã: Chỉ số định lượng giúp đo tỷ lệ mã được thực thi trong quá trình kiểm thử. Vấn đề là số lượng mã của các bài kiểm thử.

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

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

Việc sử dụng mã đảm bảo 100% 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 việc đảm bảo 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 thiếu sót.

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 đây:

/* 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ì không thực sự kiểm thử mã. Đối tượng xác nhận expect(true).toBe(true) sẽ luôn đạt 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 phù hợp có thể khiến bạn cảm thấy không an toàn. Điều này còn tệ hơn cả việc không có chỉ số nào. Ví dụ: nếu bạn có một bộ kiểm thử đạt được mức độ sử dụng mã 100% nhưng tất cả các bài kiểm thử đều vô nghĩa, thì bạn có thể cảm nhận sai về mức độ an toàn rằng mã của bạn đã được kiểm thử tốt. Nếu bạn vô tình xoá hoặc làm hỏng một phần mã xử lý ứng dụng, thì bài kiểm thử vẫn sẽ đạt, cho dù ứng dụng không còn hoạt động chính xác nữa.

Để tránh trường hợp này:

  • Bài đánh giá thử nghiệm. Viết và xem lại các chương trình kiểm thử để đảm bảo các bài kiểm thử đó có ý nghĩa và kiểm thử mã trong nhiều tình huống.
  • Sử dụng mức độ phù hợp của mã làm nguyên tắc, chứ không phải là thước đo duy nhất về mức độ hiệu quả của thử nghiệm 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 tìm hiểu kỹ hơn về cách bạn có thể sử dụng mức độ sử dụng mã trong ba loại hình kiểm thử phổ biến:

  • Kiểm thử đơn vị. Đây là loại kiểm thử phù hợp nhất để thu thập mức độ sử dụng mã vì chúng được thiết kế để kiểm thử nhiều tình huống và đường dẫn kiểm thử nhỏ.
  • Kiểm thử tích hợp. Các mô-đun này có thể giúp thu thập mức độ sử dụng mã cho cá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 độ phù hợp của một phần lớn hơn của mã nguồn và có thể khó xác định được kiểm thử nào thực sự bao gồ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 rõ ràng.
  • Kiểm thử toàn diện (E2E). Việc đo lường mức độ sử dụng mã cho kiểm thử E2E là rất khó khăn và khó khăn do tính chất phức tạp của các 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 để thực hiện. Điều này là do 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 giúp đo lường mức độ hiệu quả của các bài kiểm thử. Mã 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 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ố. Ngoài ra, hãy nhớ xem xét các yếu tố khác, chẳng hạn như chất lượng bài kiểm thử và các yêu cầu đối với đơn đăng ký.

Mục tiêu của bạn không phải là đạ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 mã ví dụ đầy đủ và các 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({});
  });
});