Integration Test — WebApplicationFactory, HttpClient, test DB
Muammodan boshlaylik
Unit testlar hammasi yashil (76–77-darslar). Deploy — va:
POST /api/buyurtmalar404 qaytaradi — route da xato- JWT sozlamasida
UseAuthenticationunutilgan — hamma 401 - EF Core so'rovi PostgreSQL da boshqacha ishlaydi — 500
- DI da servis ro'yxatdan o'tmagan — ilova ishga tushmaydi
Har bir detal alohida ishlaydi, lekin birgalikda — yo'q.
Integration test nima?
Bir nechta qismni birgalikda tekshirish: HTTP so'rov → routing → middleware → autentifikatsiya → controller → servis → haqiqiy baza → JSON javob.
Test piramidasi
/\
/E2E\ kam, sekin, qimmat
/------\
/Integr. \ o'rtacha
/----------\
/ Unit test \ ko'p, tez, arzon
/--------------\
Hayotiy o'xshatma
Avtomobil detallari stendda sinaldi (unit test). Endi ular yig'ilgan mashina poligonda sinaladi: motor uzatmalar qutisiga to'g'ri ulanganmi, tormoz pedali haqiqatan g'ildiraklarni to'xtatadimi. Har bir detal ideal bo'lsa ham, ular o'rtasidagi ulanishda xato bo'lishi mumkin.
WebApplicationFactory
dotnet add Dokon.Tests package Microsoft.AspNetCore.Mvc.Testing
Program klassini test loyihasiga ko'rinadigan qilish (top-level statements uchun):
// Program.cs oxirida
public partial class Program { }
[!INFO] .NET 10 da top-level statements uchun
Programklassi avtomatikpublicqilib generatsiya qilinadi — bu qator shart emas.
public class MahsulotlarApiTests(WebApplicationFactory<Program> factory)
: IClassFixture<WebApplicationFactory<Program>>
{
[Fact]
public async Task Royxat_200_qaytaradi()
{
var client = factory.CreateClient();
var javob = await client.GetAsync("/api/mahsulotlar");
javob.EnsureSuccessStatusCode();
var royxat = await javob.Content.ReadFromJsonAsync<List<MahsulotDto>>();
Assert.NotNull(royxat);
}
}
WebApplicationFactory butun ilovani xotirada ishga tushiradi (haqiqiy port siz, TestServer) — Program.cs dagi barcha sozlamalar bilan. HttpClient so'rovlari tarmoqsiz unga boradi.
Haqiqiy baza — Testcontainers
In-memory provayder yoki SQLite PostgreSQL dan boshqacha ishlaydi (tranzaksiyalar, case sensitivity, JSON ustunlar). Eng ishonchlisi — Docker da haqiqiy PostgreSQL:
dotnet add Dokon.Tests package Testcontainers.PostgreSql
using Testcontainers.PostgreSql;
public class DokonApiFactory : WebApplicationFactory<Program>, IAsyncLifetime
{
private readonly PostgreSqlContainer _db = new PostgreSqlBuilder()
.WithImage("postgres:17")
.Build();
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Testing");
builder.ConfigureTestServices(services =>
{
services.RemoveAll<DbContextOptions<DokonDbContext>>();
services.AddDbContext<DokonDbContext>(o => o.UseNpgsql(_db.GetConnectionString()));
});
}
public async Task InitializeAsync()
{
await _db.StartAsync();
using var scope = Services.CreateScope();
await scope.ServiceProvider.GetRequiredService<DokonDbContext>().Database.MigrateAsync();
}
public new async Task DisposeAsync() => await _db.DisposeAsync();
}
Testlar boshida konteyner ko'tariladi, migratsiyalar qo'llanadi, oxirida konteyner o'chiriladi. CI da ham ishlaydi (Docker bo'lsa).
[!INFO] xUnit v3 da
IAsyncLifetimemetodlariValueTaskqaytaradi (ValueTask InitializeAsync(),ValueTask DisposeAsync()). Yuqoridagi kod xUnit v2 uchun; v3 da qaytish turlarini moslang.
To'liq oqim testi
public class BuyurtmaOqimiTests(DokonApiFactory f) : IClassFixture<DokonApiFactory>
{
[Fact]
public async Task Mahsulot_yaratish_va_olish()
{
var client = f.CreateClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", TestToken.Admin());
var yaratish = await client.PostAsJsonAsync("/api/mahsulotlar",
new { nomi = "Test telefon", narx = 1_500_000 });
Assert.Equal(HttpStatusCode.Created, yaratish.StatusCode);
var yaratilgan = await yaratish.Content.ReadFromJsonAsync<MahsulotDto>();
var olish = await client.GetFromJsonAsync<MahsulotDto>(quot;/api/mahsulotlar/{yaratilgan!.Id}");
Assert.Equal("Test telefon", olish!.Nomi);
}
[Fact]
public async Task Tokensiz_yaratish_401()
{
var javob = await f.CreateClient().PostAsJsonAsync("/api/mahsulotlar", new { nomi = "X", narx = 1 });
Assert.Equal(HttpStatusCode.Unauthorized, javob.StatusCode);
}
[Fact]
public async Task Yaroqsiz_malumot_400_problem_details()
{
var client = f.CreateClient();
client.DefaultRequestHeaders.Authorization = new("Bearer", TestToken.Admin());
var javob = await client.PostAsJsonAsync("/api/mahsulotlar", new { nomi = "", narx = -5 });
Assert.Equal(HttpStatusCode.BadRequest, javob.StatusCode);
Assert.Equal("application/problem+json", javob.Content.Headers.ContentType?.MediaType);
}
}
TestToken.Admin() — test konfiguratsiyasidagi kalit bilan JWT yaratuvchi yordamchi (60-darsdagi TokenService ni qayta ishlatish mumkin).
Tashqi servislarni almashtirish
builder.ConfigureTestServices(services =>
{
services.RemoveAll<ITolovProvayder>();
services.AddSingleton<ITolovProvayder, SoxtaTolovProvayder>();
});
Baza — haqiqiy, to'lov va email — soxta.
Testlar orasida baza holati
Testlar bir-birining ma'lumotiga ta'sir qilmasligi kerak:
- Har bir test o'z ma'lumotini yaratadi (noyob nomlar, ID larga tayanmaslik)
- Yoki har test oldidan bazani tozalash — Respawn kutubxonasi jadvallarni tez tozalaydi
- Yoki har bir testni tranzaksiyada bajarib, oxirida rollback
Tez-tez uchraydigan xatolar
UseInMemoryDatabase ga to'liq ishonish — haqiqiy SQL xulqi (constraint, tranzaksiya) tekshirilmaydi.
Testlar tartibga bog'liq — biri yaratgan ma'lumotni boshqasi kutadi; parallel ishga tushganda yiqiladi.
Development konfiguratsiyasi bilan test — haqiqiy servislarga ulanib ketish; UseEnvironment("Testing") va almashtirishlar.
Xulosa
Integration test — qismlarni birgalikda tekshirish: routing, middleware, auth, DI, baza, JSON. WebApplicationFactory<Program> — ilovani xotirada ishga tushiradi, CreateClient() bilan haqiqiy HTTP so'rovlar; IClassFixture — umumiy fixture.
Testcontainers — Docker da haqiqiy PostgreSQL, migratsiyalar bilan. ConfigureTestServices — tashqi servislarni soxtasiga almashtirish. Status kodlar, ProblemDetails va 401/403 ni tekshiring. Testlar mustaqil: o'z ma'lumoti yoki Respawn.
Keyingi dars
79-dars: Test best practices — AAA naqshi, test nomlash, coverage