monkeyBug
monkeyBug

Reputation: 555

How to Mock IMongoDatabase

I'm using Moq for mocking my objects in an ASP.NET Core project.

I want to mock the following IsConnection() method:

public Client(IMongoClient client)
{
    _client = client;
}

public async Task<bool> IsConectionOk()
{            
    var pingCommand = new BsonDocument("ping", 1);
    var mongoDb = _client.GetDatabase("Name");
    var commandResult = await mongoDb.RunCommandAsync<BsonDocument>(pingCommand);
    return commandResult != null;        
}

As you see, there's only one injection, IMongoClient, so I need to mock this one. Now, I need to mock IMongoDatabase as well since the _client.GetDatabase returns me an IMongoDatabase which runs RunCommandAsync

this is my unit test:

[Fact]
public async Task IsConnectionOk_xxx_RunPing1Command()
{
    var dbMock = new Mock<IMongoDatabase>();
    var resultCommand = new BsonDocument("ok", 1);
    dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<BsonDocument>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(resultCommand);

    var mongoClientMock = new Mock<IMongoClient>();
    mongoClientMock.Setup(stub => stub.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>())).Returns(dbMock.Object);

    var client = new Client(mongoClientMock.Object);
    var pingCommand = new BsonDocument("ping", 1);

    //act
    await client.IsConectionOk();

    //assert
    dbMock.Verify(mock => mock.RunCommandAsync<BsonDocument>( It.Is<BsonDocument>(x => x.Equals(pingCommand)) , It.IsAny<ReadPreference>() ,It.IsAny<CancellationToken>()));
}

You can see that I mocked a IMongoDatabase so my mongoClientMock can return it when the code is executing. When code is running, I have checked that mongoClientMock.GetDatabase() is returning a MongoDatabase (good until there), the problem is that when MongoDatabaseMock calls RunCommandAsync is not returning what I set up, it just returns null. I don't know what I could be missing here, any thoughts?

Upvotes: 5

Views: 3821

Answers (2)

Nkosi
Nkosi

Reputation: 247333

Things are a little tricky here.

Some background first.

According to documentation, IMongoDatabase.RunCommandAsync<TResult> is defined as

Task<TResult> RunCommandAsync<TResult>(
    Command<TResult> command,
    ReadPreference readPreference = null,
    CancellationToken cancellationToken = null
)

Note the Command<TResult>, while in your code you pass a BsonDocument.

Luckily BsonDocument has an implicit conversion operator from BsonDocument to Command<TResult>

When a setup does not get what was configured it tends to return null. So you need to make sure that it is setup properly so that it performs the expected behavior.

[TestClass]
public class UnitTest1 {
    [TestMethod]
    public async Task _IsConnectionOk_xxx_RunPing1Command() {
        var dbMock = new Mock<IMongoDatabase>();
        var resultCommand = new BsonDocument("ok", 1);
        dbMock
            .Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<Command<BsonDocument>>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>()))
            .ReturnsAsync(resultCommand)
            .Verifiable();

        var mongoClientMock = new Mock<IMongoClient>();
        mongoClientMock
            .Setup(stub => stub.GetDatabase(It.IsAny<string>(), It.IsAny<MongoDatabaseSettings>()))
            .Returns(dbMock.Object);

        var client = new Client(mongoClientMock.Object);
        var pingCommand = new BsonDocument("ping", 1);

        //act
        var actual = await client.IsConectionOk();

        //assert
        Assert.IsTrue(actual);
        dbMock.Verify();
    }
}

Upvotes: 4

monkeyBug
monkeyBug

Reputation: 555

Just found my problem, take a look at the next line:

dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<BsonDocument>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(resultCommand);

turns out that RunCommandAsync first parameter is a Command<TResult> so the fix I needed was:

dbMock.Setup(stub => stub.RunCommandAsync<BsonDocument>(It.IsAny<Command<BsonDocument>>(), It.IsAny<ReadPreference>(), It.IsAny<CancellationToken>())).ReturnsAsync(anyResultCommand);

And problem solved!

Upvotes: 1

Related Questions