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

Sam Storie picture Sam Storie · Feb 4, 2015 · Viewed 9.2k times · Source

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?

Answer

Andriy Tolstoy picture Andriy Tolstoy · Oct 30, 2017

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