如何使用Moq模拟StackExchange.Redis ConnectionMultiplexer类?

Sam*_*rie 8 .net c# unit-testing moq stackexchange.redis

我正在努力模拟与StackExchange.Redis库相关的行为,但无法弄清楚如何正确模拟它使用的密封类.一个具体的例子是我的调用代码我正在做这样的事情:

    var cachable = command as IRedisCacheable;

    if (_cache.Multiplexer.IsConnected == false)
    {
        _logger.Debug("Not using the cache because the connection is not available");

        cacheAvailable = false;
    }
    else if (cachable == null)
    {
Run Code Online (Sandbox Code Playgroud)

其中的关键行是_cache.Multiplexer.IsConnected我正在检查以确保在使用缓存之前我有一个有效的连接.所以在我的测试中,我想用这样的方式模拟这种行为:

    _mockCache = new Mock<IDatabase>();
    _mockCache.Setup(cache => cache.Multiplexer.IsConnected).Returns(false);
Run Code Online (Sandbox Code Playgroud)

但是,虽然该代码编译得很好,但在运行测试时出现此错误:

测试中抛出了Moq异常

我也试过模拟多路复用器本身,并将其提供给我的模拟缓存,但我遇到了多路复用器类被密封的事实:

    _mockCache = new Mock<IDatabase>();
    var mockMultiplexer = new Mock<ConnectionMultiplexer>();
    mockMultiplexer.Setup(c => c.IsConnected).Returns(false);
    _mockCache.Setup(cache => cache.Multiplexer).Returns(mockMultiplexer.Object);
Run Code Online (Sandbox Code Playgroud)

...但是会导致此错误:

尝试模拟密封类时抛出的错误

最终我想在我的测试中控制该属性是真还是假,所以有没有正确的方法来模拟这样的东西?

Han*_*ney 12

在我看来,最好的方法是将你所有的Redis交互包装在你自己的类和接口中.像CacheHandler : ICacheHandler和的东西ICacheHandler.你的所有代码都只会说话ICacheHandler.

这样,您就消除了对Redis的强烈依赖(您可以根据需要更换实现ICacheHandler).您还可以模拟与缓存层的所有交互,因为它是针对接口编程的.

你不应该StackExchange.Redis直接测试- 这不是你写的代码.

  • 虽然我同意添加一个包装器类,但这种方法是不是只是将问题移回一层?如果我无法模拟其行为,如何为我的包装类(ICacheHandler)编写单元测试,该类调用redis方法? (4认同)
  • ICacheHandler实现的测试将是集成测试而不是单元测试. (2认同)

And*_*toy 6

使用接口IConnectionMultiplexer而不是您自己的类中的具体类ConnectionMultiplexer.

public interface ICacheable
{
   void DoYourJob();
}

public sealed class RedisCacheHandler : ICacheable
{
    private readonly IConnectionMultiplexer multiplexer;

    public RedisCacheHandler(IConnectionMultiplexer multiplexer)
    {
        this.multiplexer = multiplexer;
    }

    public void DoYourJob() 
    {
        var database = multiplexer.GetDatabase(1);

        // your code        
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以轻松地模拟和测试它:

// Arrange
var mockMultiplexer = new Mock<IConnectionMultiplexer>();

mockMultiplexer.Setup(_ => _.IsConnected).Returns(false);

var mockDatabase = new Mock<IDatabase>();

mockMultiplexer
    .Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
    .Returns(mockDatabase.Object);

var cacheHandler = new RedisCacheHandler(mockMultiplexer.Object);

// Act
cacheHandler.DoYourJob();


// Assert
// your tests
Run Code Online (Sandbox Code Playgroud)