Yaygın olarak kullanılan dört kod kapsamı türü

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ı, 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ı.

Dört tür metin 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:

  1. coffeeName Onay işareti. ile calcCoffeeIngredient aranacak
  2. coffeeName ve cup ile calcCoffeeIngredient aranıyor Onay işareti.
  3. Kahve Espresso Onay işareti.
  4. Kahve Americano X işareti.
  5. Diğer kahve Onay işareti.

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?

Aşağıdaki satırdır: 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 benzetmeyle açıklamak gerekirse: 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.

Tam kod örneğine ve iyi kod kapsamına sahip testlere bakın. 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({});
  });
});