How to Mock IMongoDatabase - c#

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?

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();
}
}

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!

Related

Redis StringSetAsync returns false in Unit Tests using Moq

I am currently writing unit tests for a RedisProvider Class for my Solution. I have a method in my class that sets a key in the Redis Cache:
public async Task<bool> StringSetAsync(string key, string value)
{
var cache = multiplexer.GetDatabase();
//Some other stuff
result = await cache.StringSetAsync(key, value);
return result;
}
In my unit tests I have the following setup:
var mockMultiplexer = new Mock<IConnectionMultiplexer>();
mockMultiplexer.Setup(_ => _.IsConnected).Returns(true);
mockMultiplexer.Setup(_ => _.Configuration).Returns(MockedData.CacheConnectionString);
var mockDatabase = new Mock<IDatabase>();
mockMultiplexer
.Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
.Returns(() => mockDatabase.Object);
RedisCacheProvider mockedCacheProvider = new RedisCacheProvider(new Mock<ILogger<RedisCacheProvider>>
().Object, mockMultiplexer.Object);
var result = await mockedCacheProvider.StringSetAsync(It.IsAny<string>(), It.IsAny<string>());
When I run the above code for my unit test, the result that I receive is always false. Is there a way to get a true result? What is wrong over here?
I realized that this should be mocked by the services using the class rather than validating this on the class itself.
Here you go:
mockedCacheProvider.Setup(h => h.StringSetAsync(
It.IsAny<string>(),
It.IsAny<string>())).ReturnsAsync(() => true);

Unit test pass string argument any string value except null or empty

I am unit testing controller logic. I have a controller action which takes a string parameter like so:
public async Task<IActionResult> Get(string searchCriterion)
{
if (string.IsNullOrEmpty(searchCriterion))
{
// Log modelstate errors
return BadRequest("Hello");
}
// etc...
}
Please note the guard which checks for string.IsNullOrEmpty(searchCriterion).
In my unit tests I like to use It.IsAny<string>():
// Act
var result = await controller.GetSearchNetworkAsync(It.IsAny<string>());
The guard means I cannot use this because It.IsAny<string>() means that the test string could indeed by null ot empty.
My question is: can I pass the argument, in my unit test, that corresponds to "any string value except null or empty"?
Edit
Please find the full unit test...
[Fact]
public async Task GetSearchNetworkAsync_ReturnsOkWithNetworkListViewModelCollection_Rn()
{
// Arrange
var mockRepo = new Mock<IUserRepository>();
mockRepo.Setup(repo => repo.GetUserAndNetworkAsync(It.IsAny<string>()))
.ReturnsAsync(GetOwnUserProfile());
mockRepo.Setup(repo => repo.SearchBirdersToFollowAsync(It.IsAny<ApplicationUser>(), It.IsAny<string>(), It.IsAny<IEnumerable<string>>()))
.ReturnsAsync(GetListOfApplicationUsers(3));
var mockUnitOfWork = new Mock<IUnitOfWork>();
var controller = new UserController(_mapper, mockUnitOfWork.Object, _logger.Object, mockRepo.Object);
controller.ControllerContext = new ControllerContext()
{
HttpContext = new DefaultHttpContext() { User = GetTestClaimsPrincipal() }
};
// Act
//var result = await controller.GetSearchNetworkAsync(It.IsAny<string>());
var result = await controller.GetSearchNetworkAsync(It.Is<string>(str => !string.IsNullOrEmpty(str)));
// Assert
var objectResult = result as ObjectResult;
Assert.NotNull(objectResult);
Assert.IsType<OkObjectResult>(result);
Assert.True(objectResult is OkObjectResult);
Assert.Equal(StatusCodes.Status200OK, objectResult.StatusCode);
Assert.IsType<List<NetworkUserViewModel>>(objectResult.Value);
var model = objectResult.Value as List<NetworkUserViewModel>;
Assert.Equal(3, model.Count);
}
It.IsAny<string>() is meant to be used only as part of the setup for mocks.
mock.Setup(_ => _.SomeMember(It.IsAny<string>()).Returns(SomeValue);
It is not meant to be used outside of the Setup expression and will return the default value for the provided generic argument.
ie
It.IsAny<string>() == null
Use an actual string to pass the desired value into your subject under test.
//Arrange
//...
string searchCriterion = "Any String You Want Here";
// Act
var result = await controller.GetSearchNetworkAsync(searchCriterion);
What are you trying to test? If the test is not checking the guard then pass an expected string value
[Fact]
public async Task GetSearchNetworkAsync_WithValidString_ReturnsOkWithNetworkListViewModelCollection_Rn()
{
...
// Act
var result = await controller.GetSearchNetworkAsync("some value");
You would then have an different test to check for the guard
[Fact]
public async Task GetSearchNetworkAsync_WithNullString_ReturnsBadRequest()
{
...
// Act
var result = await controller.GetSearchNetworkAsync(null);
or
[Fact]
public async Task GetSearchNetworkAsync_WithEmptyString_ReturnsBadRequest()
{
...
// Act
var result = await controller.GetSearchNetworkAsync(string.empty);

Mocking async query operations

I created unit test and I have to mock context of my EF. I use Moq library and Xunit. I have a test method like this:
[Fact]
public async Task DeleteAttachmentCommandHandler_WithValidCommand_ShouldCallSaveChangesAsyncOnce()
{
var command = new DeleteAttachmentCommand { Id = Guid.NewGuid() };
var attachments = new List<Attachment>();
var dbSetMock = attachments.AsQueryable().BuildMockDbSetForAsyncQueryOperations();
_context.Setup(x => x.Set<Attachment>()).Returns(dbSetMock.Object);
_context.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1).Verifiable();
await Act(command);
_context.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once);
}
The _context is of type Mock<IEmployeeSettlementsDbContext>
The BuildMockDbSetForAsyncQueryOperations is my extension method thanks to the MSDN documentation -> https://learn.microsoft.com/en-us/ef/ef6/fundamentals/testing/mocking, which uses the async providers like TestDbAsyncEnumerable, TestDbAsyncEnumerator, TestDbAsyncQueryProvider. And my extension BuildMockDbSetForAsyncQueryOperations looks like this:
public static Mock<IDbSet<TEntity>> BuildMockDbSetForAsyncQueryOperations<TEntity>(this IQueryable<TEntity> data) where TEntity : class
{
var dbSetMock = new Mock<IDbSet<TEntity>>();
dbSetMock.As<IDbAsyncEnumerable<TEntity>>().Setup(x => x.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator()));
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(data.Provider));
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.Expression).Returns(data.Expression);
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.ElementType).Returns(data.ElementType);
dbSetMock.As<IQueryable<TEntity>>().Setup(x => x.GetEnumerator()).Returns(data.GetEnumerator());
return dbSetMock;
}
In my actual handler method which is testing I have a line:
var attachment = await _context.Set<Attachment>()
.SingleAsync(x => x.Id == command.Id);
And when I run the test it fails and show me the message
Message: System.InvalidOperationException : The sequence does not contain a matching element.
But when I change the query in handler to be ListAsync() instead of SingleAsync() then the mock is setup correctly and returns me an empty list. But it does not work for the single element using SingleAsync().
EDIT:
Here is my full Handler method:
public async Task<Unit> Handle(DeleteAttachmentCommand command, CancellationToken cancellationToke = default(CancellationToken))
{
ValidateCommand(command);
var attachment = await _context.Set<Attachment>().SingleAsync(x => x.Id == command.Id);
attachment.IsDeleted = true;
await _context.SaveChangesAsync();
return Unit.Value;
}
SingleAsync() documentation in MSDN
Asynchronously returns the only element of a sequence, and throws an
exception if there is not exactly one element in the sequence.
Consider to use FirstOrDefault()or FirstOrDefaultAsync() method instead of SingleAsync(). Here and here are a link about that, this will not throw exception.

Unit tests not showing code coverage with async/await

I'm using vs2017 and none of my unit tests are showing code coverage. I"m using async/await and MOQ.
[TestMethod]
[TestCategory("SeriesRepository")]
public void GetSeriesAsyncShouldReturnSeriesId12345()
{
var repositoryMock = new Mock<ISeriesRepository>();
var seriesId = "12345";
var channel = 75864;
var mockSeries = new Mock<ISeries>();
mockSeries.SetupGet(x => x.SeriesId).Returns("12345");
repositoryMock
.Setup(x => x.GetSeriesAsync(seriesId, channel))
.ReturnsAsync(mockSeries.Object);
var result = repositoryMock.Object.GetSeriesAsync(seriesId, channel).Result;
result.SeriesId.Should().Be(seriesId);
}
First fact is that you are not actually testing anything in your test as you simply create mocks and invoke the mocks.
You are simply testing that the mocking framework works as advertised.
Secondly the tests can be made async as well to allow the test to exercise sequentially.
[TestMethod]
[TestCategory("SeriesRepository")]
public async Task GetSeriesAsyncShouldReturnSeriesId12345() {
var repositoryMock = new Mock<ISeriesRepository>();
var seriesId = "12345";
var channel = 75864;
var mockSeries = new Mock<ISeries>();
mockSeries.Setup(_ => _.SeriesId).Returns(seriesId);
repositoryMock
.Setup(_ => _.GetSeriesAsync(seriesId, channel))
.ReturnsAsync(mockSeries.Object);
var result = await repositoryMock.Object.GetSeriesAsync(seriesId, channel);
result.SeriesId.Should().Be(seriesId);
}
What is suppose to happen is that you mock the dependencies of a target under test to behave as expected in order to verify some desired behavior
Lets say we wanted to test a target method of a class. Something like
public class SeriesService {
private readonly ISeriesRepository repository;
public SeriesService(ISeriesRepository repository
this.repository = repository;
}
public async Task<ISeries> GetSeries(string seriesId, int channel) {
var series = await repository.GetSeriesAsync(seriesId, channel);
//...do some other stuff
return series;
}
}
An example test would then look like this
[TestMethod]
[TestCategory("SeriesRepository")]
public async Task TargetMethodShouldReturnSeriesId12345() {
//Assert
var repositoryMock = new Mock<ISeriesRepository>();
var seriesId = "12345";
var channel = 75864;
var mockSeries = new Mock<ISeries>();
mockSeries.Setup(_ => _.SeriesId).Returns(seriesId);
repositoryMock
.Setup(_ => _.GetSeriesAsync(seriesId, channel))
.ReturnsAsync(mockSeries.Object);
var target = new SeriesService(repositoryMock.Object);
//Act
var result = await target.GetSeries(seriesId, channel);
//Assert
result.Should().NotBeNull();
result.SeriesId.Should().Be(seriesId);
}
There isn't any wait for your repositoryMock setup to complete. So, your test case is ending before your async calls are complete.
Make sure you wait by adding:
repositryMock.setup(...).ReturnsAsync(...).GetAwaiter().GetResult();
Or
repositryMock.setup(...).ReturnsAsync(...).Result;
Or
repositryMock.setup(...).ReturnsAsync(...).Wait();
This should solve your problem.

Async Await generating a null exception in testing

I've done a lot of heavy modification to some async code, and I've been tinkering with it long enough that I am in need of a second set of eyes.
In a unit test, I'm calling my async code, and then validating the values it should return. The object that ultimately performs the async operation is mocked.
The Unit Test:
public void GetCharactersAsync_ById_Test()
{
var charDictionary = TestHelper.GetCharactersIdDictionary(1);
var mockReq = new Mock<IRequester>();
mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
(It.IsAny<string>())).ReturnsAsync(charDictionary);
var char1 = TestHelper.GetCharacter();
var api = GameObject.GetInstance(mockReq.Object);
var character = api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);
Assert.AreEqual(character.Result.Name, char1.Name);
}
GetCharacterAsync:
public async Task<Character> GetCharacterAsync(Region region, int charId)
{
var res = await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>>
(bld.BuildUrlForEndpoint("GetCharacter",
region, charId.ToString()));
var obj = res.Result.Values.FirstOrDefault();
return obj;
}
requester.CreateGetRequestAsync (though this should be mocked and not matter):
public async Task<T> CreateGetRequestAsync<T>(string urlString)
{
return await this.GetResultAsyncDeserialized<T>(new HttpRequestMessage(HttpMethod.Get, urlString));
}
and finally, GetResultAsyncDeserialized (though this is mocked and shouldn't matter):
protected async Task<T> GetResultAsyncDeserialized<T>(HttpRequestMessage request)
{
var result = string.Empty;
using (var response = await httpClient.GetAsync(request.RequestUri))
{
if (!response.IsSuccessStatusCode)
{
HandleRequestFailure(response.StatusCode);
}
using (var content = response.Content)
{
result = await content.ReadAsStringAsync();
}
}
return JsonConvert.DeserializeObject<T>(result);
}
I have next to zero experience with async, and this code is very similar to the old working code in structure (I moved a few things around, like the JsonConvert to the bottom layer when it used to be up top). The major changes, in fact, were all at the level that is being mocked, and shouldn't impact the unit test. The unit test is almost identical to the old one (removed some serialization/deserialization).
Exception is a Null Reference Exception on GetCharacterAsync line:
var obj = res.Result.Values.FirstOrDefault();
Your await requester.CreateGetRequestAsync<Task<Dictionary<long, Character>>> is wrong, you should not be passing Task in to that function, the method signature should be await requester.CreateGetRequestAsync<Dictionary<long, Character>>
public async Task<Character> GetCharacterAsync(Region region, int charId)
{
var res = await requester.CreateGetRequestAsync<Dictionary<long, Character>>
(bld.BuildUrlForEndpoint("GetCharacter",
region, charId.ToString()));
var obj = res.Values.FirstOrDefault();
return obj;
}
The reason you are getting null is in GetCharacterAsync you call CreateGetRequestAsync<Task<Dictionary<long, Character>>> but your moq was set up with mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
P.S. As a side note, you really should not likely be doing character.Result.Name in your test, make the test return async Task instead of void and await the result of api.GetCharacterAsync. Pretty much every unit test library (including the one built in to visual studio) supports async/await now.
public async Task GetCharactersAsync_ById_Test()
{
var charDictionary = TestHelper.GetCharactersIdDictionary(1);
var mockReq = new Mock<IRequester>();
mockReq.Setup(x => x.CreateGetRequestAsync<Dictionary<long, Character>>
(It.IsAny<string>())).ReturnsAsync(charDictionary);
var char1 = TestHelper.GetCharacter();
var api = GameObject.GetInstance(mockReq.Object);
var character = await api.GetCharacterAsync(TestHelper.GetRegion(), (int)char.Id);
Assert.AreEqual(character.Name, char1.Name);
}

Categories

Resources