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 inceleyeceğiz.
Kod kapsamı nedir?
Kod kapsamı, testlerinizin yürüttüğü kaynak kod 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 | % İfadeleri | % Branch | İşlev Yüzdesi | % Satır | Açık olmayan satırlar |
---|---|---|---|---|---|
file.js | %90 | %100 | %90 | %80 | 89.256 |
coffee.js | %55,55 | %80 | %50 | %62,5 | 10-11, 18 |
Siz yeni özellikler ve testler ekledikçe kod kapsamı yüzdelerini artırmak, uygulamanızın ayrıntılı bir şekilde test edildiğinden emin olmanızı sağlayabilir. 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 bir kod kapsamı türünün yüzdesini nasıl hesapladığını görmek için, kahve içeriklerini hesaplamak üzere aşağıdaki kod örneğini göz önünde bulundurun:
/* 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ülmeden kalırsa bu, kodun bir kısmının test edilmemiş olduğu anlamına gelir.
Kod örneğinde sekiz satırlık yürütülebilir kod (kırmızı ve yeşil renkle vurgulanmıştır) ancak testler americano
koşulunu (iki satır) ve isValidCoffee
işlevini (tek 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 {};
}
…
Şube kapsamı, yürütülen dalların veya koddaki karar noktalarının (ör. ifadeler 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. Yani şube kapsamı %80.
Hesap özeti 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);
}
Ekstre kapsamı, testlerinizin yürüttüğü kodda ifadelerin yüzdesini ölçer. İlk bakışta "Bu, satır kapsamıyla aynı mı?" diye sorabilirsiniz. Aslında, ifade kapsamı satır kapsamına benzer, ancak birden fazla ifade içeren tek satırlık kodları dikkate alır.
Kod örneğinde sekiz satır yürütülebilir kod ancak 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?
Kod kapsamı araçlarının çoğu, aşağıdaki 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 ifade kapsamı, basit ve anlaşılması kolay bir metrik olduğundan iyi bir başlangıç noktasıdır. İfade kapsamının aksine dal kapsamı ve işlev kapsamı, testlerin bir koşulu (dal) mı yoksa işlevi mi çağırdığını ölçer. Bu nedenle, ifadeden sonra 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. İlgili risk düzeyinin belirlenmesine 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: 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.
Kodların% 100 kapsamına alınması, hata meydana gelmediği 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 zahmetli bir iştir. Bunun nedeni, E2E testlerinin kaynak koda odaklanmak yerine testinizin gerekliliklerini kapsamasıdır. Bu nedenle, kod kapsamını kullanmak yerine gereksinim kapsamı daha iyi bir seçenek olabilir.
Sonuç
Kod kapsamı, testlerinizin ne kadar etkili olduğunu ölçmek için yararlı bir metrik olabilir. Kodunuzdaki kritik mantığın iyi bir şekilde test edilmesini sağlayarak uygulamanızın kalitesini artırmanıza yardımcı olabilir.
Ancak, kod kapsamının tek 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çlanmamıştır. Bunun yerine, kod kapsamının yanı sıra birim testleri, entegrasyon testleri, uçtan uca testler ve manuel testler gibi çeşitli test yöntemlerini içeren çok yönlü bir test planı 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({});
});
});