اختبار التكامل C# مع Docker: مستوى متقدم
اختبار التكامل هو مرحلة حاسمة في تطوير C#، حيث يسمح بالتحقق من تفاعل مكونات النظام مع التبعيات الحقيقية: قواعد البيانات، قوائم انتظار الرسائل، واجهات API الخارجية. أصبح استخدام Docker لهذا الغرض معيارًا صناعيًا، ولكن على المستوى المتقدم تظهر أسئلة حول العزل والأداء والتنفيذ المتوازي. في هذه المقالة، سنستعرض التقنيات الاحترافية لاختبار التكامل C# مع Docker باستخدام مكتبة Testcontainers والتكوينات المتقدمة.
1. إعداد Testcontainers لـ .NET: البنية التحتية الأساسية
تتيح مكتبة Testcontainers لـ .NET إدارة حاويات Docker برمجيًا في بيئة الاختبار. على عكس التشغيل اليدوي لـ docker-compose، يقوم Testcontainers تلقائيًا بتشغيل وإيقاف الحاويات لكل فئة اختبار أو وحدة، مما يضمن نقاء البيئة.
قم بتثبيت الحزمة عبر NuGet:
dotnet add package Testcontainers --version 3.9.0dotnet add package Testcontainers.PostgreSql --version 3.9.0dotnet add package Testcontainers.MsSql --version 3.9.0مثال لفئة أساسية لاختبارات التكامل مع PostgreSQL:
using Testcontainers.PostgreSql;using Xunit;
public class DatabaseFixture : IAsyncLifetime{ private readonly PostgreSqlContainer _container = new PostgreSqlBuilder() .WithImage("postgres:15-alpine") .WithDatabase("testdb") .WithUsername("testuser") .WithPassword("testpass") .Build();
public string ConnectionString => _container.GetConnectionString();
public async Task InitializeAsync() { await _container.StartAsync(); }
public async Task DisposeAsync() { await _container.StopAsync(); }}الآن يمكن لكل فئة اختبار استخدام هذا المثبت للحصول على نسخة معزولة من قاعدة البيانات.
2. عزل الاختبارات وإدارة حالة الحاويات
يتطلب المستوى المتقدم تحكمًا دقيقًا في العزل. بدلاً من حاوية واحدة لمجموعة الاختبارات بأكملها، استخدم حاوية واحدة لكل فئة اختبار أو حتى لكل اختبار. هذا يمنع حالة قاعدة البيانات "القذرة" بين الاختبارات.
التنفيذ عبر السمة Collection في xUnit:
[CollectionDefinition("Database collection")]public class DatabaseCollection : ICollectionFixture<DatabaseFixture>{}
[Collection("Database collection")]public class UserRepositoryTests{ private readonly DatabaseFixture _fixture;
public UserRepositoryTests(DatabaseFixture fixture) { _fixture = fixture; }
[Fact] public async Task CreateUser_ShouldPersistUser() { // Arrange await using var context = new AppDbContext(_fixture.ConnectionString); await context.Database.EnsureCreatedAsync(); var repo = new UserRepository(context);
// Act var user = new User { Name = "Test User" }; await repo.CreateAsync(user);
// Assert var savedUser = await context.Users.FindAsync(user.Id); Assert.NotNull(savedUser); }}للعزل الكامل على مستوى الاختبار، استخدم ITestOutputHelper وأنشئ الحاوية داخل طريقة DisposeAsync:
public class IsolatedTest : IAsyncLifetime{ private PostgreSqlContainer _container;
public async Task InitializeAsync() { _container = new PostgreSqlBuilder().Build(); await _container.StartAsync(); }
public async Task DisposeAsync() { await _container.DisposeAsync(); }}3. التشغيل المتوازي للاختبارات وإدارة الموارد
أحد التحديات الرئيسية