Kod kapsamının ne olduğunu öğrenin ve bunu ölçmenin yaygın dört yolunu keşfedin.
"Kod kapsamı" ifadesini duydunuz mu? Bu yayında, testlerde kod kapsamının ne olduğunu ve bunu ölçmenin yaygın dört yolunu ele alacağız.
Kod kapsamı nedir?
Kod kapsamı, testlerinizin yürüttüğü kaynak kodu yüzdesini ölçen bir metriktir. Bu sayede, düzgün test edilmemiş alanları belirleyebilirsiniz.
Bu metrikleri kaydetme işlemi genellikle şu şekilde görünür:
Dosya | % ifadeleri | % Branch | İşlev Yüzdesi | % Satır | Kapsam dışındaki hatlar |
---|---|---|---|---|---|
file.js | %90 | %100 | %90 | %80 | 89.256 |
coffee.js | %55,55 | %80 | %50 | %62,5 | 10-11, 18 |
Yeni özellikler ve testler eklerken kod kapsamı yüzdelerini artırarak uygulamanızın kapsamlı bir şekilde test edildiğinden emin olabilirsiniz. Ancak keşfedilecek daha çok şey var.
Yaygın olarak kullanılan dört kod kapsamı türü
Kod kapsamını toplamanın ve hesaplamanın dört yaygın yolu vardır: işlev, satır, dal ve ifade kapsamı.
Her kod kapsamı türünün yüzdesini nasıl hesapladığını görmek için kahve bileşenlerini hesaplayan aşağıdaki kod örneğini inceleyin:
/* 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);
}
calcCoffeeIngredient
işlevini doğrulayan testler şunlardır:
/* 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({});
});
});
Bu canlı demoda kodu ve testleri çalıştırabilir veya depoyu inceleyebilirsiniz.
İşlev kapsamı
Kod kapsamı: %50
/* coffee.js */
export function calcCoffeeIngredient(coffeeName, cup = 1) {
// ...
}
function isValidCoffee(name) {
// ...
}
İşlev kapsamı basit bir metriktir. Kodunuzdaki testlerinizin çağırdığı işlevlerin yüzdesini yakalar.
Kod örneğinde iki işlev vardır: calcCoffeeIngredient
ve isValidCoffee
. Testler yalnızca calcCoffeeIngredient
işlevini çağırır. Bu nedenle işlev kapsamı %50'dir.
Satır kapsamı
Kod kapsamı: %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);
}
Satır kapsamı, test grubunuzun yürüttüğü yürütülebilir kod satırlarının yüzdesini ölçer. Bir kod satırı yürütülmezse kodun bir kısmı test edilmemiştir.
Kod örneğinde sekiz satır yürütülebilir kod (kırmızı ve yeşil renkle vurgulanmıştır) vardır ancak testler americano
koşulunu (iki satır) ve isValidCoffee
işlevini (bir satır) yürütmez. Bu, satır kapsamının %62,5 olmasına neden olur.
Satır kapsamı, yürütülebilir olmadıkları için function isValidCoffee(name)
ve let espresso, water;
gibi bildirim ifadelerini dikkate almaz.
Şube kapsamı
Kod kapsamı: %80
/* coffee.js */
export function calcCoffeeIngredient(coffeeName, cup = 1) {
// ...
if (coffeeName === 'espresso') {
// ...
return { espresso };
}
if (coffeeName === 'americano') {
// ...
return { espresso, water };
}
return {};
}
…
Kol kaplama, kodda yürütülen kolların veya karar noktalarının (ör. if ifadeleri veya döngüler) yüzdesini ölçer. Testlerin koşullu ifadelerin hem doğru hem de yanlış dallarını inceleyip incelemeyeceğini belirler.
Kod örneğinde beş dal vardır:
coffeeName
ilecalcCoffeeIngredient
aranacakcoffeeName
vecup
ilecalcCoffeeIngredient
aranıyor- Kahve Espresso
- Kahve Americano
- Diğer kahve
Testler, Coffee is Americano
koşulu hariç tüm dalları kapsar. Dolayısıyla şube kapsamı %80'dir.
Beyan kapsamı
Kod kapsamı: %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);
}
İfade kapsamı, kodunuzdaki testlerinizin yürüttüğü ifadelerin yüzdesini ölçer. İlk bakışta "Bu satır kapsamıyla aynı değil mi?" diye düşünebilirsiniz. Gerçekten de ifade kapsamı, satır kapsamına benzer ancak birden fazla ifade içeren tek satır kodları hesaba katar.
Kod örneğinde sekiz satır yürütülebilir kod olmasına rağmen dokuz ifade vardır. İki ifade içeren satırı görebiliyor musunuz?
espresso = 30 * cup; water = 70 * cup;
Testler dokuz ifadeden yalnızca beşini kapsadığından ifade kapsamı %55, 55'tir.
Her satır için her zaman bir ifade yazarsanız satır kapsamınız, ifade kapsamınıza benzer.
Ne tür bir kod kapsamı seçmelisiniz?
Çoğu kod kapsamı aracı, bu dört yaygın kod kapsamı türünü içerir. Öncelik verilecek kod kapsamı metriğinin seçimi, belirli proje gereksinimlerine, geliştirme uygulamalarına ve test hedeflerine bağlıdır.
Genel olarak, basit ve anlaşılması kolay bir metrik olduğu için beyan kapsamı iyi bir başlangıç noktasıdır. Cümle kapsamının aksine, dal kapsamı ve işlev kapsamı, testlerin bir koşulu (dal) mu yoksa işlevi mi çağırdığını ölçer. Bu nedenle, beyan kapsamının ardından doğal bir ilerlemedir.
Yüksek beyan kapsamına ulaştıktan sonra şube kapsamına ve işlev kapsamına geçebilirsiniz.
Test kapsamı, kod kapsamıyla aynı mıdır?
Hayır. Test kapsamı ve kod kapsamı genellikle karıştırılır ancak bunlar farklıdır:
- Test kapsamı: Test paketinin yazılımın özelliklerini ne kadar iyi kapsadığını ölçen nitel bir metriktir. Bu, risk seviyesini belirlemenize yardımcı olur.
- Kod kapsamı: Test sırasında çalıştırılan kodun oranını ölçen nicel bir metriktir. Testlerin ne kadar kod kapsaması gerektiğiyle ilgilidir.
Basitleştirilmiş bir benzetme yapalım: Bir web uygulamasını bir ev olarak düşünün.
- Test kapsamı, testlerin evdeki odaları ne kadar iyi kapsadığını ölçer.
- Kod kapsamı, testlerin evin ne kadarını kapsadığını ölçer.
%100 kod kapsamı, hata olmadığı anlamına gelmez
Test sırasında yüksek kod kapsamı elde etmek kesinlikle arzu edilir olsa da% 100 kod kapsamı, kodunuzda hata veya kusur bulunmadığını garanti etmez.
%100 kod kapsamı elde etmenin anlamsız bir yolu
Aşağıdaki testi ele alalım:
/* 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
});
});
Bu test, işlev, satır, dal ve ifade kapsamını% 100 oranında karşılar ancak aslında kodu test etmediği için anlamlı değildir. expect(true).toBe(true)
beyanı, kodun düzgün çalışıp çalışmamasından bağımsız olarak her zaman geçer.
Kötü bir metrik, hiç metrik olmamasından daha kötüdür
Kötü bir metrik, size yanlış bir güvenlik hissi verebilir. Bu, hiç metrik olmamasından daha kötüdür. Örneğin, %100 kod kapsamı sağlayan ancak testlerinin tümü anlamsız olan bir test paketiniz varsa kodunuzun iyi test edildiğine dair yanlış bir güvenlik hissi edinebilirsiniz. Uygulama kodunun bir bölümünü yanlışlıkla siler veya bozarsanız uygulama artık düzgün çalışmasa bile testler yine de geçer.
Bu senaryoyu önlemek için:
- Test incelemesi. Anlamlı olduklarından emin olmak için testler yazın ve inceleyin ve kodu çeşitli farklı senaryolarda test edin.
- Test etkinliği veya kod kalitesinin tek ölçütü olarak değil, kod kapsamını bir kılavuz olarak kullanın.
Farklı test türlerinde kod kapsamını kullanma
Kod kapsamını yaygın üç test türüyle nasıl kullanabileceğinizi daha ayrıntılı bir şekilde inceleyelim:
- Birim testleri. Birden fazla küçük senaryoyu ve test yolunu kapsayacak şekilde tasarlandıkları için kod kapsamı elde etmek için en iyi test türüdür.
- Entegrasyon testleri. Entegrasyon testleri için kod kapsamı toplamanıza yardımcı olabilirler ancak bunları dikkatli bir şekilde kullanın. Bu durumda, kaynak kodun daha büyük bir kısmının kapsamını hesaplarsınız ve hangi testlerin aslında kodun hangi bölümlerini kapsadığını belirlemek zor olabilir. Bununla birlikte, entegrasyon testlerinin kod kapsamını hesaplamak, iyi izole edilmiş birimleri olmayan eski sistemler için yararlı olabilir.
- Uçtan uca (E2E) testler. E2E testlerinin karmaşık yapısı nedeniyle bu testlerin kod kapsamını ölçmek zor ve zorlu bir iştir. Kod kapsamı yerine şart kapsamı kullanmak daha iyi bir seçenek olabilir. Bunun nedeni, uçtan uca testlerin odak noktasının kaynak koda değil, testinizin koşullarını kapsamaya yönelik olmasıdır.
Sonuç
Kod kapsamı, testlerinizin ne kadar etkili olduğunu ölçmek için yararlı bir metrik olabilir. Kodunuzdaki önemli mantığın iyi test edilmesini sağlayarak uygulamanızın kalitesini artırmanıza yardımcı olabilir.
Ancak kod kapsamının yalnızca bir metrik olduğunu unutmayın. Testlerinizin kalitesi ve uygulama gereksinimleriniz gibi diğer faktörleri de göz önünde bulundurun.
%100 kod kapsamı hedeflemek amaç değildir. Bunun yerine, birim testleri, entegrasyon testleri, uçtan uca testler ve manuel testler gibi çeşitli test yöntemlerini içeren kapsamlı bir test planıyla birlikte kod kapsamını kullanmalısınız.
İyi kod kapsamına sahip kod örneğinin tamamını ve testlerini inceleyin. Kodu ve testleri bu canlı demo ile de çalıştırabilirsiniz.
/* 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({});
});
});