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 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 | Câu lệnh phần trăm | % Branch | Hàm% | % Dòng | Các tuyến không được quảng cáo |
---|---|---|---|---|---|
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 còn có thể khám phá nhiều tính năng khác.
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 toán mức độ sử dụng mã: mức độ sử dụng hàm, dòng, nhánh và câu lệnh.
Để xem cách mỗi loại mức độ sử dụng mã tính toán tỷ lệ phần trăm, hãy xem xét ví dụ về mã sau đây để tính toán các 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: calcCoffeeIngredient
và isValidCoffee
. 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 độ bao phủ dòng
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 đánh dấu 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)
và 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ã:
- Gọi
calcCoffeeIngredient
chỉ bằngcoffeeName
- Gọi
calcCoffeeIngredient
bằngcoffeeName
vàcup
- Cà phê là Espresso
- Cà phê là Americano
- Cà phê khác
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
. Do đó, mức độ bao phủ nhánh là 80%.
Phạm vi câu lệnh
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. Thoạt nhìn, bạn có thể thắc mắc "đây có phải là mức độ sử dụng dòng không?". Thật vậy, mức độ sử dụng câu lệnh tương tự như mức độ sử dụng dòng nhưng tính đến các 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?
espresso = 30 * cup; water = 70 * cup;
Các chương trình kiểm thử chỉ bao gồm 5 trong số 9 câu lệnh, do đó, mức độ bao phủ 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ư mức độ sử dụng câu lệnh, mức độ sử dụng nhánh và mức độ sử dụng hàm đo lường xem các chương trình kiểm thử có gọi một điều kiện (nhánh) hay một hàm. Do đó, các chỉ số 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 mức độ bao phủ 100% 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ã. Câu nhận định expect(true).toBe(true)
sẽ luôn vượt qua bất kể mã có hoạt động chính xác hay không.
Chỉ số không chính xác 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 thử. 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 công cụ này có thể giúp thu thập mức độ sử dụng mã cho các bài kiểm thử tích hợp, nhưng hãy sử dụng chúng một cách thận trọng. Trong trường hợp này, bạn sẽ tính toán 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 kiểm thử tích hợp có thể hữu ích cho 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 kiểm thử E2E rất khó khăn và đầy thách thức do bản 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ã, 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ụ đầy đủ về mã và kiểm thử với phạm vi 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({});
});
});