اختبار التكامل في C# مع Docker: كيفية التخلص من مشاكل البيئة
كل مطور C# واجه هذه الحالة: الاختبارات تنجح على الجهاز المحلي لكنها تفشل على خادم CI. السبب غالباً هو اختلافات في البيئة: إصدار قاعدة البيانات، إعدادات Redis، خدمات اختبار مفقودة. اختبار التكامل في C# مع Docker يحل هذه المشكلة نهائياً. في هذا المقال، سنرى كيفية إعداد بيئة اختبار معزولة باستخدام الحاويات، كتابة اختبارات تكامل حقيقية، ودمجها في خط أنابيب CI/CD.
لماذا Docker هو الأداة المثالية لاختبارات تكامل C#
اختبار التكامل يفحص التفاعل بين مكونات النظام: التطبيق + قاعدة البيانات، API + ذاكرة التخزين المؤقت، الخدمات المصغرة مع بعضها البعض. بدون Docker، ستحتاج إلى:
- تثبيت PostgreSQL، SQL Server، Redis على كل جهاز مطور
- تكوين خادم CI بنفس الإصدارات
- تنظيف البيانات بين مرات تشغيل الاختبار
Docker يحل كل هذه المهام. تصف البنية التحتية في كود (Dockerfile، docker-compose.yml)، وتنشر بشكل متطابق على أي نظام. بالنسبة لـ C# (.NET) هذا مهم بشكل خاص لأن Microsoft تدعم الحاويات بنشاط: صور SDK .NET الرسمية، SQL Server في حاوية Linux، التكامل مع Azure DevOps.
إعداد المشروع: Testcontainers + xUnit
لاختبار التكامل في C# مع Docker، سنستخدم مكتبة Testcontainers (متوفرة على NuGet). تدير تلقائياً دورة حياة الحاوية: تبدأ قبل الاختبارات، تتوقف بعد الانتهاء.
الخطوة 1. تثبيت الحزم
أنشئ مشروعاً جديداً للاختبارات أو افتح مشروعاً قائماً. أضف حزم NuGet:
dotnet add package Testcontainersdotnet add package Testcontainers.PostgreSqldotnet add package xunitdotnet add package FluentAssertionsالخطوة 2. الفئة الأساسية للاختبارات مع الحاويات
لننشئ فئة مجردة ستشغل PostgreSQL قبل تشغيل الاختبارات:
using Testcontainers.PostgreSql;using Xunit;
public abstract class IntegrationTestBase : IAsyncLifetime{ private readonly PostgreSqlContainer _postgresContainer;
protected string ConnectionString => _postgresContainer.GetConnectionString();
protected IntegrationTestBase() { _postgresContainer = new PostgreSqlBuilder() .WithImage("postgres:15-alpine") .WithDatabase("testdb") .WithUsername("test") .WithPassword("test123") .Build(); }
public async Task InitializeAsync() { await _postgresContainer.StartAsync(); }
public async Task DisposeAsync() { await _postgresContainer.StopAsync(); }}الفئة تطبق IAsyncLifetime من xUnit — الحاوية ستبدأ قبل أول اختبار وتتوقف بعد آخر اختبار. طريقة GetConnectionString() ترجع المنفذ الديناميكي الذي خصصته Docker للحاوية.
الخطوة 3. كتابة اختبار تكامل
الآن لنكتب اختباراً يتحقق من عمل المستودع مع قاعدة بيانات حقيقية:
public class UserRepositoryTests : IntegrationTestBase{ [Fact] public async Task CreateUser_Should_SaveToDatabase() { // Arrange await using var connection = new NpgsqlConnection(ConnectionString); await connection.OpenAsync(); var repository = new UserRepository(connection); var user = new User { Name = "Ivan", Email = "ivan@example.com" };
// Act var userId = await repository.CreateAsync(user);
// Assert userId.Should().BeGreaterThan(0); var savedUser = await repository.GetByIdAsync(userId); savedUser.Should().NotBeNull(); savedUser.Name.Sho