Test best practices — AAA pattern, naming, coverage, TDD kirish

~15 daq · .NET Backend · Test

Muammodan boshlaylik

Loyihada 800 ta test bor. Lekin:

  • Test1, Test2, BuyurtmaTest — yiqilganda nima buzilganini bilish uchun kodni o'qish kerak
  • 30 ta test har safar emas, ba'zan yiqiladi — jamoa ularni e'tiborsiz qoldira boshladi
  • Har bir refactoring da 50 ta test sinadi, garchi xulq o'zgarmagan bo'lsa ham
  • Coverage 90%, lekin haqiqiy xatolar baribir production ga yetib boryapti

Testlar bor — ishonch yo'q.


Hayotiy o'xshatma

Tutun detektori. Yaxshi detektor haqiqiy yong'inda albatta chalinadi va tuxum qovurganingizda chalinmaydi. Har kuni behuda chalinadigan detektorni odamlar o'chirib qo'yadi — va haqiqiy yong'in kuni u jim turadi. Ishonchsiz test — o'chirib qo'yilgan detektordan ham yomon: u xavfsizlik hissini beradi.


1. AAA — aniq tuzilma

[Fact]
public void Savat_mahsulot_qoshilsa_jami_oshadi()
{
    // Arrange
    var savat = new Savat();
    var telefon = new Mahsulot { Narx = 1_000_000 };

    // Act
    savat.Qosh(telefon, soni: 2);

    // Assert
    Assert.Equal(2_000_000, savat.Jami);
}
  • Bitta Act — test bitta xulqni tekshiradi
  • Arrange katta bo'lsa — yordamchi metod yoki builder

2. Nomlash — test hujjat bo'lsin

Shablon: Metod_Sharoit_KutilganNatija

❌ ✅
Test1 Hisobla_vip_mijoz_5_foiz_qoshimcha_chegirma
BuyurtmaTest Rasmiylash_omborda_yetarli_emas_xato_tashlaydi
TestLogin Login_notogri_parol_401_qaytaradi

Yiqilgan test nomini o'qib, kodga qaramasdan nima buzilganini tushunish kerak:

Failed: Rasmiylash_omborda_yetarli_emas_xato_tashlaydi

3. Xulqni tekshiring, amalga oshirishni emas

// ❌ ichki tafsilotga bog'langan
_repo.Verify(r => r.OlAsync(5, It.IsAny<CancellationToken>()), Times.Once);
_repo.Verify(r => r.Qosh(It.IsAny<Buyurtma>()), Times.Once);
_repo.Verify(r => r.SaqlaAsync(It.IsAny<CancellationToken>()), Times.Once);

// ✅ kuzatiladigan natija
var buyurtma = await service.RasmiylashAsync(dto, ct);
Assert.Equal(BuyurtmaHolati.Tolangan, buyurtma.Holat);
Assert.Equal(250_000, buyurtma.Summa);

Kod ichki tuzilishi o'zgarsa (refactoring), lekin xulqi bir xil qolsa — test sinmasligi kerak.


4. Testlar mustaqil va deterministik

// ❌ vaqtga bog'liq — ba'zan yiqiladi
Assert.True(token.Tugaydi > DateTime.UtcNow);

// ✅ vaqtni boshqarish
var vaqt = new FakeTimeProvider(new DateTimeOffset(2026, 11, 8, 12, 0, 0, TimeSpan.Zero));
var service = new TokenService(vaqt);
var token = service.Yarat();
vaqt.Advance(TimeSpan.FromMinutes(16));
Assert.True(service.Eskirganmi(token));

"Ba'zan" yiqiladigan (flaky) testlar sabablari:

  • Joriy vaqt va sana
  • Tasodifiy sonlar (seed siz)
  • Testlar o'rtasidagi umumiy holat (statik o'zgaruvchilar, bitta baza yozuvlari)
  • Task.Delay bilan "kutib turish"
  • Tashqi servislar va tarmoq

[!WARNING] Flaky testni "qayta ishga tushirsa o'tadi" deb qoldirmang — darhol tuzating yoki vaqtincha Skip bilan o'chirib, vazifa oching. Bitta flaky test butun test to'plamiga ishonchni yo'qotadi.


5. Test ma'lumotlari — builder

public class BuyurtmaBuilder
{
    private int _mijozId = 1;
    private decimal _summa = 100_000;
    private BuyurtmaHolati _holat = BuyurtmaHolati.Yangi;

    public BuyurtmaBuilder Mijoz(int id) { _mijozId = id; return this; }
    public BuyurtmaBuilder Summa(decimal s) { _summa = s; return this; }
    public BuyurtmaBuilder Bekor() { _holat = BuyurtmaHolati.Bekor; return this; }

    public Buyurtma Yarat() => new() { MijozId = _mijozId, Summa = _summa, Holat = _holat };
}

var b = new BuyurtmaBuilder().Summa(2_000_000).Bekor().Yarat();

Test faqat muhim qiymatlarni ko'rsatadi; qolganlari — mantiqli sukut qiymatlari.


6. Coverage — vosita, maqsad emas

dotnet test --collect:"XPlat Code Coverage"
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:"**/coverage.cobertura.xml" -targetdir:"coverage" -reporttypes:Html
Coverage nima beradi Nima bermaydi
Qaysi kod umuman ishlamaganini ko'rsatadi Testlar to'g'ri narsani tekshirishini kafolatlamaydi
Test qilinmagan muhim tarmoqlarni topish Assert siz test ham coverage ni oshiradi

[!TIP] Coverage ni qat'iy maqsad (masalan, "100%") qilmang — jamoa ma'nosiz testlar yozib raqamni ko'taradi. Muhim biznes mantiq (to'lov, chegirma, ruxsatlar) yaxshi qoplanganiga e'tibor bering; oddiy DTO va konfiguratsiyaga vaqt sarflamang. Testlar sifatini tekshirish uchun — mutation testing (Stryker.NET): kodni ataylab buzib, testlar buni ushlashini tekshiradi.


7. Tezlik va tuzilma

  • Unit testlar — butun to'plam soniyalarda; sekin bo'lsa, dasturchilar ishga tushirmay qo'yadi
  • Integration testlar — alohida loyiha yoki kategoriya ([Trait("Kategoriya", "Integration")])
  • Test loyihasi tuzilmasi asosiy loyihani takrorlaydi: Services/ChegirmaHisoblagichTests.cs
  • CI da har bir PR da barcha testlar
dotnet test --filter "Kategoriya!=Integration"     # tez lokal ishga tushirish

8. Xato topilsa — avval test

Production da xato topildi:

  1. Xatoni takrorlaydigan yiqiladigan test yozing
  2. Kodni tuzating — test yashil
  3. Bu xato endi hech qachon jimgina qaytmaydi

Tez-tez uchraydigan xatolar

Testda mantiq (if, sikllar, hisoblash) — testning o'zida xato bo'lishi mumkin; kutilgan qiymatni to'g'ridan-to'g'ri yozing.

Private metodlarni test qilish — public xulq orqali tekshiring.

Assert siz test — hech narsani tekshirmaydi, faqat exception yo'qligini.


Xulosa

Yaxshi test — ishonchli detektor: xato bo'lsa yiqiladi, bo'lmasa — yo'q. AAA tuzilmasi, bitta Act; nom — Metod_Sharoit_Natija, hujjat vazifasini bajaradi. Xulqni tekshiring, ichki amalga oshirishni emas — refactoring testlarni sindirmasin.

Mustaqil va deterministik: vaqt (FakeTimeProvider), tasodif, umumiy holatni boshqaring; flaky testni darhol tuzating. Builder lar — o'qiladigan test ma'lumotlari. Coverage — vosita, maqsad emas; muhim mantiqqa e'tibor, mutation testing. Xato topilsa — avval yiqiladigan test.


Keyingi bo'lim

80-dars: Docker — .NET ilovani konteynerga solish, Dockerfile