首页 > 解决方案 > 如何为 MediatR 处理程序 C# 编写单元测试

问题描述

我正在尝试使用 Moq 框架为我的 MediatR 处理程序之一编写单元测试。我有以下代码。简而言之,此处理程序的作用是查询给定键并使用 EF Core 返回包含键及其值的响应。还有一个基本的缓存机制。如果在缓存中找到密钥,则从缓存中提取并返回。

处理程序

public GetConfigByKeyRequestHandler(MyContext context, ICacheProvider cacheProvider, IOptions<CacheConfigs> cacheConfigs)
{
    this.context = context;
    this.cacheProvider = cacheProvider;
    this.cacheConfigs = cacheConfigs?.Value;
}


public async Task<ConfigResponse> Handle(GetConfigByKeyRequest request, CancellationToken cancellationToken)
{
    ConfigResponse config;
    if (!await cacheProvider.ExistsAsync(request.Key))
    {
        config = await context.Configs
            .Where(x.ConfigKey.Equals(request.Key))
            .Select(x => 
                    new ConfigResponse {
                        ConfigKey = x.ConfigKey,
                        ConfigValue = x.ConfigValue 
                    })
            .FirstOrDefaultAsync(cancellationToken);

        if (config is not null)
        {
            await cacheProvider.PutAsync(new CacheItem<ConfigResponse>(request.Key, config), new CacheOptions
            {
                ExpireAfter = TimeSpan.FromMinutes(cacheConfigs.ExpireAfterInMinutes).TotalMilliseconds,
                ExpireInactive = TimeSpan.FromMinutes(cacheConfigs.ExpireInActiveInMinutes).TotalMilliseconds
            }); 
        }
        return config;
    }

    config = await cacheProvider.PullAsync<ConfigResponse>(request.Key);
    return config;
}

我认为我应该涵盖两种不同的场景:

  1. 当在缓存中找到键时
  2. 当在缓存中找不到键并且从 DbContext 返回时。

单元测试

private Mock<ICacheProvider> cacheProviderMock;
private IOptions<CacheConfigs> cacheConfigs;
public GetConfigByKeyRequestHandlerTests()
{
    cacheProviderMock = new Mock<ICacheProvider>();
    cacheConfigs = Options.Create(
        new CacheConfigs
        {
            ExpireAfterInMinutes = 3,
            ExpireInActiveInMinutes = 3
        });
}

[Fact]
public async Task GetConfigByKeyHandler_WhenKeyIsCached_ShouldReturnConfigByKey()
{
    // arrange 
    var options = new DbContextOptionsBuilder<MyContext>().UseInMemoryDatabase("MyInMemoryDatabase").Options;
    var configItems = Enumerable.Range(0, 5).Select(x => new Config
    {
        ConfigKey = $"key{x}",
        ConfigValue = $"value{x}"
    });

    using (var context = new MyContext(options))
    {
        await context.Configs.AddRangeAsync(configItems);
        await context.SaveChangesAsync();
    }

   
    using (var context = new MyContext(options))
    {
        cacheProviderMock.Setup(x => x.ExistsAsync(It.IsAny<string>())).Returns(Task.FromResult(true));
        cacheProviderMock.Setup(x => x.PullAsync<ConfigResponse>("key2"))
            .Returns(Task.FromResult(new ConfigResponse
            {
                ConfigKey = "key2",
                ConfigValue = "value2"
            }));
        var getConfigByKeyHandler = new GetConfigByKeyRequestHandler(context, cacheProviderMock.Object, cacheConfigs);
        var getConfigByKeyRequest = new GetConfigByKeyRequest("key2");
        // act
        var result = await getConfigByKeyHandler.Handle(getConfigByKeyRequest, CancellationToken.None);

        // assert
        Assert.NotNull(result);
        Assert.Equal("key2", result.ConfigKey);
    }

}
...
...

使用相同的逻辑,我对未缓存密钥的另一种情况进行了另一项测试

...
...

[Fact]
public async Task GetConfigByKeyHandler_WhenKeyIsNotCached_ShouldReturnConfigByKey()
{
    // arrange 
    var options = new DbContextOptionsBuilder<MyContext>().UseInMemoryDatabase("MyInMemoryDatabase").Options;
    var configItems = Enumerable.Range(0, 5).Select(x => new Config
    {
        ConfigKey = $"key{x}",
        ConfigValue = $"value{x}"
    });

    using (var context = new MyContext(options))
    {
        await context.Configs.AddRangeAsync(configItems);
        await context.SaveChangesAsync();
    }

    
    using (var context = new MyContext(options))
    {
        cacheProviderMock.Setup(x => x.ExistsAsync(It.IsAny<string>())).Returns(Task.FromResult(false));
        var getConfigByKeyHandler = new GetConfigByKeyRequestHandler(context, cacheProviderMock.Object, cacheConfigs);
        var getConfigByKeyRequest = new GetConfigByKeyRequest("key2");
        // act
        var result = await getConfigByKeyHandler.Handle(getConfigByKeyRequest, CancellationToken.None);

        // assert
        Assert.NotNull(result);
        Assert.Equal("key2", result.ConfigKey);
    }


}

我已经编写了 2 个单元测试,涵盖了我上面提到的场景,但我不确定它们是否合理,我不确定是否应该以这种方式进行测试。你有什么建议我应该为我上面分享的处理程序编写什么/如何编写测试?

标签: c#unit-testingmoqmediatr

解决方案


推荐阅读