در مقالات قبلی به نوشتن تست با استقاده از xUnit و نوشتن تست برای APIها پرداختیم. اما هنگام نوشتن تست‌ها در بعضی موارد نیازمند ارتباط با دیتابیس هستیم که دیتایی را از آن بخوانیم و یا رکوردهایی را در آن ذخیره کنیم. اگر از یک دیتابیس ثابت استفاده کنیم، هربار که تست‌ها اجرا شوند مقادیر موجود در دیتابیس تغییر خواهند کرد و اگر شخص دیگری با دیتابیس کار کند و دیتاهای آن را تغییر دهد، احتمال آنکه تست‌ها با موفقیت اجرا نشوند زیاد است.

کتابخانه testcontainer این امکان را به ما می‌دهد که قبل از اجرای تست ها، یک دیتابیس تست درون داکر ایجاد نماییم، دیتاهای اولیه را وارد کنیم، مایگریشن‌ها را اجرا کنیم و سپس تست‌ها اجرا شوند و در نهایت دیتابیس ایجاد شده پاک شود. این کتابخانه دیتابیس مورد نیاز ما را درون داکر اجرا میکند و بعد از اجرای تست کانتینر را پاک میکنند.
در این مطلب یک نمونه تست برای Redis پیاده سازی می‌کنیم. ابتدا یک کلاس به نام RedisCacheRepository برای افزودن و دریافت اطلاعات از ردیس ایجاد می‌کنیم:

public class RedisCacheRepository : ICacheRepository
{
    private readonly IDatabase _datebase;
    public RedisCacheRepository(IConnectionMultiplexer connectionMultiplexer)
    {
        _datebase = connectionMultiplexer.GetDatabase();
    }

    public async Task<T> GetAsync<T>(string key)
    {
        var redisValue = await _datebase.StringGetAsync(key);

        if (string.IsNullOrWhiteSpace(redisValue))
            return default;

        return JsonConvert.DeserializeObject<T>(redisValue);
    }

    public async Task SetAsync<T>(string key, T value,  TimeSpan expireTime)
    {
        var redisValue = JsonConvert.SerializeObject(value);

        await _datebase.StringSetAsync(key, redisValue, expireTime);
    }
}

در ادامه باید یک کلاس ایجاد کنیم که IAsyncLifeTime  را ایمپلیمنت کرده باشد و در متدهای DisposeAsync و InitializeAsync دستورات مربوط به اجرا کردن کانتینر و پاک کردن آن را وارد نماییم. در این کلاس می‌توانید کانکشن مربوط به ردیس را نیز ایجاد نمایید و در کلاس‌‎های تست، از آن کانکشنن استفاده کنید:

public class RedisInitializer : IAsyncLifetime
{
    private readonly RedisTestcontainer _redisTestcontainer;

    public IConnectionMultiplexer RedisConnection { get; private set; }
    public RedisInitializer()
    {
        _redisTestcontainer = new TestcontainersBuilder<RedisTestcontainer>()
           .WithDatabase(new RedisTestcontainerConfiguration
           {
               Port = 6379
           })
           .WithImage("redis:5.0.14")
           .WithCleanUp(true)
           .WithExposedPort(6379)
           .WithPortBinding(6379, true)
           .Build();
    }
    public async Task DisposeAsync()
    {
        await _redisTestcontainer.StopAsync();
    }

    public async Task InitializeAsync()
    {
        await _redisTestcontainer.StartAsync();

        RedisConnection = ConnectionMultiplexer
            .Connect(_redisTestcontainer.ConnectionString);
    }
}

بعد از اجرا شدن متد StartAsync مربوط به RedisTestcontainer پراپرتی RedisConnection که از نوع IConnectionMultiplexer می‌باشد را مقداردهی اولیه می‌کنیم تا در تست ها از آن استفاده کنیم.
اکنون می‌توانیم تست‌های مربوط به RedisCacheRepository را پیاده سازی نماییم:

[Collection("Sequential")]
public class RedisCacheRepositoryTest : IClassFixture<RedisInitializer>
{
    private readonly ICacheRepository _cacheRepository;

    public RedisCacheRepositoryTest(RedisInitializer redisInitializer)
    {
        _cacheRepository = new RedisCacheRepository(redisInitializer.RedisConnection);
    }

    [Fact]
    public async Task When_CacheKeyIsNotExist_Then_ForReferenceTypesNullShouldReturned()
    {
        var invalidCacheKey = "invalidCacheKey";

        var result = await _cacheRepository.GetAsync<UserModel>(invalidCacheKey);

        Assert.Null(result);
    }

    [Fact]
    public async Task When_CacheTimeIsExpired_Then_CacheRepositoryShouldReturnDefaultValue()
    {
        var cacheKey = "Test";

        var data = 10;

        var expireTime = TimeSpan.FromSeconds(10);

        await _cacheRepository.SetAsync(cacheKey, data, expireTime);

        var cacheValue = await _cacheRepository.GetAsync<int>(cacheKey);

        Assert.Equal(data, cacheValue);

        await Task.Delay(expireTime);// wait to expire cache

        cacheValue = await _cacheRepository.GetAsync<int>(cacheKey);

        Assert.Equal(0, cacheValue);
    }
}

تست اول مربوط به دریافت مقدار پیشفرض برای رفرنس تایپ‌ها می‌باشد که در صورت عدم وجود دیتا در ردیس، مقدار نال برگشت داده شود و تست بعدی مربوط به پاک شدن دیتای ردیس بعد از اکسپایر تایم می‌باشد.
اکنون اگر تست‌ها را اجرا نمایید قبل از اجرای تست‌ها کانتینر مربوط به ردیس باید اجرا شود و بعد از اجرای تست‌ها کانتینرهای ایجاد شده باید پاک شوند.
با استفاده از کتابخانه testcontainer میتوانید برای این دیتابیس‌ها تست نویسی ایجاد کنید:

Powered by Froala Editor

نظرات