How to use Moq to mock up the StackExchange.Redis ConnectionMultiplexer class?

放肆的年华 提交于 2021-01-27 04:06:24

问题


I am working to mock up behaviors related to the StackExchange.Redis library, but can't figure out how to properly mock the sealed classes it uses. A specific example is in my calling code I'm doing something like this:

    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)
    {

The key line in there is _cache.Multiplexer.IsConnected where I'm checking to make sure I have a valid connection before using the cache. So in my tests I want to mock up this behavior with something like this:

    _mockCache = new Mock<IDatabase>();
    _mockCache.Setup(cache => cache.Multiplexer.IsConnected).Returns(false);

However, while that code compiles just fine, I get this error when running the test:

Moq exception thrown within the test

I have also tried mocking the multiplexer class itself, and providing that to my mocked cache, but I run into the fact the multiplexer class is sealed:

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

...but that results in this error:

The error thrown when trying to mock a sealed class

Ultimately I want to control whether that property is true or false in my tests, so is there a correct way to mock up something like this?


回答1:


The best approach in my opinion is to wrap all of your Redis interaction in your own class and interface. Something like CacheHandler : ICacheHandler and ICacheHandler. All of your code would only ever speak to ICacheHandler.

This way, you eliminate a hard dependency on Redis (you can swap out the implementation of ICacheHandler as you please). You can also mock all interaction with your caching layer because it's programmed against the interface.

You should not test StackExchange.Redis directly - it is not code you've written.




回答2:


Use the interface IConnectionMultiplexer instead of the concrete class ConnectionMultiplexer in your own class.

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        
    }
}

Then you could easily mock and test it:

// 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



回答3:


Not included in the above answer is the more detailed Setup of the mockDatabase instance. I struggled a little bit finding a working example of something as simple as mocking the IDatabase StringGet method (e.g., handling of optional parameters, using RedisKey vs string, using RedisValue vs string, etc.), so thought I would share. Here is what worked for me.

This test setup:

var expected = "blah";
RedisValue expectedValue = expected;

mockDatabase.Setup(db => db.StringGet(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
.Returns(expectedValue);

To affect what is returned by this tested method call:

var redisValue = _connectionMultiplexer.GetDatabase().StringGet(key);


来源:https://stackoverflow.com/questions/28325870/how-to-use-moq-to-mock-up-the-stackexchange-redis-connectionmultiplexer-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!