Here is the method I want to write test for:
public class JobStore : IJobStore
{
private readonly IMongoDbContext _context;
public JobStore(IMongoDbContext context)
{
_context = context;
}
public async Task<IJob> CreateAsync(IJob job)
{
await _context.Jobs.InsertOneAsync(job as Job);
return job;
}
}
Here is my test :
public class JobStoreTest
{
private readonly Mock<IMongoDbContext> _moqIMongoDbContext;
private readonly JobStore _jobStore;
public JobStoreTest()
{
_moqIMongoDbContext = new Mock<IMongoDbContext>();
_jobStore = new JobStore(_moqIMongoDbContext.Object);
_moqIMongoDbContext
.Setup(_ => _.Jobs.InsertOneAsync(It.IsAny<Job>(), It.IsAny<CancellationToken>()))
.Returns((IJob x) => Task.FromResult(x));
}
[Theory]
[ClassData(typeof(JobClassesForTesting))]
public async Task CreateAsync(IJob job)
{
var result = await _jobStore.CreateAsync(job);
Assert.Equal(job,result as Job);
}
}
Here is the result of test:
System.ArgumentException
Invalid callback. Setup on method with 2 parameter(s) cannot invoke callback with different number of parameters (1).
Here is the JobClassesForTestingClass which is my scenario for testing :
public class JobClassesForTesting : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
{
yield return new object[]
{
new Job()
{
Payload = null,
EntityName = "EntityNameTest1",
Module = "ModuleTest1",
MetaData = new Dictionary<string, string>(),
Categories = new List<JobCategory>{new JobCategory {Name = "CategoryTest1"} },
PublishedDate = DateTime.Now
}
};
yield return new object[]
{
new Job()
{
Payload = "PayloadTest2",
EntityName = "EntityNameTest2",
Module = "ModuleTest2",
MetaData = new Dictionary<string, string>(),
Categories = new List<JobCategory>{new JobCategory {Name = "CategoryTest2"} },
PublishedDate = DateTime.Now
}
};
yield return new object[]
{
new Job()
{
Payload = "PayloadTest3",
EntityName = "EntityNameTest3",
Module = "ModuleTest3",
MetaData = new Dictionary<string, string>(),
Categories = new List<JobCategory>{new JobCategory {Name = "CategoryTest3"} },
PublishedDate = DateTime.Now
}
};
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
I want the result of the test would be as same as each of my Job objects but the result of the test is System.ArgumentException
The exception clearly states the problem about the arguments count mismatch.
Refactor the mock setup
_moqIMongoDbContext
.Setup(_ => _.Jobs.InsertOneAsync(
It.IsAny<Job>(),
It.IsAny<InsertOneOptions>(),
It.IsAny<CancellationToken>())
)
.Returns((Job document, InsertOneOptions options, CancellationToken token)
=> Task.FromResult(document));
Note how the parameters in the Returns delegate/callback now matches the number and types of argument matchers used by the member that was setup.
IMongoCollection.InsertOneAsync Method (TDocument, InsertOneOptions, CancellationToken)
Task InsertOneAsync(
TDocument document,
InsertOneOptions options = null,
CancellationToken cancellationToken = null
)
Reference Moq Quickstart to get a better understanding of how to use that mocking library
Related
I am trying to run unit tests using Moq. I am fairly new to Moq and I have ran into a problem with this current unit test.
I have a controller that is fetching some items. The result is encapsulated within a generic interface using ICollection.
public interface IResult
{
}
public interface IListResult : ICollection<IResult>
{
}
My controller is simply calling a method that returns the result.
[HttpGet("get/{userId}/{pageSize}/{fetchNext}")]
public IActionResult GetConversations(int userId, int pageSize, bool fetchNext)
{
try
{
GetConversationsByUserIdQuery query = new GetConversationsByUserIdQuery()
{
UserId = userId,
PageSize = pageSize,
FetchNext = fetchNext
};
var result = _mediator.GetConversationsByUserId(query);
return Ok(result);
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}
}
When I am running my unit test, the result always comes back null and I am failing to understand what I am missing in my set up.
[Theory]
[InlineData(1, 2 , false)]
[InlineData(1, 2 , true)]
public async void When_AddMessageToNewConversation_ThenSuccessfullyAdd(int userId, int pageSize, bool fetchNext)
{
// Arrange
Mock<IMessageMediator> _mediator = new Mock<IMessageMediator>();
Mock<IListResult> _listResult = new Mock<IListResult>();
GetConversationsByUserIdQuery query = new GetConversationsByUserIdQuery()
{
UserId = userId,
PageSize = pageSize,
FetchNext = fetchNext
};
List<Conversation> expected = new List<Conversation>()
{
new Conversation()
{
Id = Guid.NewGuid(),
Created_At = DateTimeOffset.Now
}
};
_listResult.Setup(x =>
x.GetEnumerator())
.Returns(expected.GetEnumerator());
GetConversationsByUserIdController controller = new GetConversationsByUserIdController(_mediator.Object);
_mediator.Setup(x =>
x.GetConversationsByUserId(query)).Returns(_listResult.Object);
// Act
IActionResult result = controller.GetConversations(userId, pageSize, fetchNext);
// Assert
Assert.True(result is OkResult);
}
The IMessageMediator is simply a delegator that handles implementation of queriers and commands. The IListResult is returned from a querier handler.
public class GetConversationsByUserIdQueryHandler:
IQueryHandler<GetConversationsByUserIdQuery>
{
private IRepository<Conversations_By_UserId>
_conversationByUserIdRepository;
private IListResult _result;
public GetConversationsByUserIdQueryHandler(IRepository<Conversations_By_UserId> conversationByUserIdRepository,
IListResult result)
{
_conversationByUserIdRepository = conversationByUserIdRepository;
_result = result;
}
public IListResult Handle(GetConversationsByUserIdQuery query)
{
IEnumerable<Conversations_By_UserId> conversations = _conversationByUserIdRepository
.Get(query.PageSize,
query.FetchNext,
message => message.UserId == query.UserId).ToList();
if (conversations.Any())
{
foreach (Conversations_By_UserId m in conversations)
{
_result.Add(new Conversation()
{
Created_At = m.Created_At,
Id = m.Id,
});
}
}
return _result;
}
}
You don't need to mock both IMessageMediator and IListResult.
You should mock dependencies - it is IMessageMediator in your case - and your can setup it to return any result.
The other thing - you mock _mediator using query created in test, but in controller you create different query (references are different) and it is the reason of null result.
Change your test:
[Theory]
[InlineData(1, 2 , false)]
[InlineData(1, 2 , true)]
public async void When_AddMessageToNewConversation_ThenSuccessfullyAdd(int userId, int pageSize, bool fetchNext)
{
// Arrange
Mock<IMessageMediator> _mediator = new Mock<IMessageMediator>();
List<Conversation> expected = new List<Conversation>()
{
new Conversation()
{
Id = Guid.NewGuid(),
Created_At = DateTimeOffset.Now
}
};
GetConversationsByUserIdController controller = new GetConversationsByUserIdController(_mediator.Object);
// It.IsAny<GetConversationsByUserIdQuery>()) instead of query
_mediator.Setup(x => x.GetConversationsByUserId(It.IsAny<GetConversationsByUserIdQuery>()))
// create fake result to return from mediator (no need to mock IListResult
.Returns(new ListResult() ...); // just create instance of IListResult with needed values
// Act
IActionResult result = controller.GetConversations(userId, pageSize, fetchNext);
// Assert
Assert.True(result is OkResult);
}
I'm trying to make a simple mock tests in .Net 5.0 and NUnit. It's working for everything, but not for an specific method... and I can't find the solution.
First I have a logic class:
public class GameBusiness : IGameBusiness
{
private readonly IGameRepository _gameRepository;
public GameBusiness(IGameRepository gameRepository)
{
_gameRepository = gameRepository;
}
public async Task<Game> CreateGame()
{
var drawCard = GenerateRandomCardValue();
var newGame = new Game() { LastDrawedCard = drawCard };
return await _gameRepository.Insert(newGame);
}
public async Task<Game> GetGame(int gameId)
{
return await _gameRepository.GetById(gameId);
}
private int GenerateRandomCardValue()
{
var random = new Random();
return random.Next(1, 13);
}
}
And in My Test Class, I Have
private readonly Mock<IGameRepository> _gameRepository = new Mock<IGameRepository>();
private readonly GameBusiness _gameBusiness;
public GameRepositoryTest()
{
_gameBusiness = new GameBusiness(_gameRepository.Object);
}
[SetUp]
public void Setup()
{
}
[Test]
public async Task GetGame_ShouldReturnGame_WhenGameExists()
{
var gameId = 5;
var game = new Game()
{
Id = 5,
LastDrawedCard = 3
};
_gameRepository.Setup(x => x.GetById(gameId)).ReturnsAsync(game);
var gameResult = await _gameBusiness.GetGame(5);
Assert.AreEqual(gameId, gameResult.Id);
}
[Test]
public async Task CreateGame_ShouldReturnNewGameWithLastDrawedCard()
{
var newGame = new Game()
{
Id = 5,
LastDrawedCard = 3
};
_gameRepository.Setup(x => x.Insert(new Game())).ReturnsAsync(newGame);
var newGameResult = await _gameBusiness.CreateGame();
Assert.IsTrue(newGame.LastDrawedCard > 0);
}
}
So my problem is that inside CreateGame_ShouldReturnNewGameWithLastDrawedCard() I create a mock that should return to me a Game object assing to newGame. But it ALWAYS return null.
When debuggin, the line in CreateGame() is returning null
return await _gameRepository.Insert(newGame);
For others methods, like
_gameRepository.Setup(x => x.GetById(gameId)).ReturnsAsync(game);
var gameResult = await _gameBusiness.GetGame(5);
It works perfect. What I'm missing here?
The issue is that the arguments do not match what was set up so it will return null by default.
Refactor to use an argument matcher and also to capture the model that was created within the member under test
[Test]
public async Task CreateGame_ShouldReturnNewGameWithLastDrawedCard() {
//Arrange
_gameRepository
.Setup(x => x.Insert(It.IsAny<Game>())) //<-- uses a catch-all matcher
.ReturnsAsync((Game arg) => arg); //<-- captures passed argument and returns it
//Act
var actual = await _gameBusiness.CreateGame();
//Assert
Assert.IsTrue(actual != null);
Assert.IsTrue(actual.LastDrawedCard > 0);
}
Reference Moq Quickstart
I want to Test the Method FindAsync(Expression<Func>) from the MongoDB C# Driver. My goal is it to
test my expression, I don't want to Mock the FindAsync Method itself. In EF I would Mock the DB Set and return my own Enumerator which is filled with my IEnumerable with test data. Can I do something like that in MongoDB also?
I using the Driver Version 2.10.4
And the Framework Moq 4.14.5
The problem you have here is FindAsync(Expression<Func>) is an extention method that just creates a new ExpressionFilterDefinition with your expression and passes it on.
Expression<Func<TDocument, bool>> filter = // some expression.
collection.FindAsync(new ExpressionFilterDefinition<TDocument>(filter), options, cancellationToken);
So you'll need to mock out the following method on IMongoCollection<TDocument>
public override Task<IAsyncCursor<TProjection>> FindAsync<TProjection>(
FilterDefinition<TDocument> filter,
FindOptions<TDocument, TProjection> options,
CancellationToken cancellationToken = default (CancellationToken));
Here's a quick example using Moq
var list = new List<Class>
{
new Class {Id = "1", Name = "Name1"},
new Class {Id = "2", Name = "Name2"},
new Class {Id = "3", Name = "Name3"}
};
var collection = new Mock<IMongoCollection<Class>>();
collection.Setup(x => x.FindAsync<Class>(
It.IsAny<FilterDefinition<Class>>(),
It.IsAny<FindOptions<Class, Class>>(),
It.IsAny<CancellationToken>()
)).ReturnsAsync((FilterDefinition<Class> filter,
FindOptions<Class, Class> options,
CancellationToken cancellationToken) =>
{
// We'll need to get back the expression
var expressionFilter = (ExpressionFilterDefinition<Class>)filter;
// Filtered our mocked list
var filtererList = list.Where(expressionFilter.Expression.Compile())
.ToList();
// Return a stubbed cursor with our filtered list
return new StubAsyncCursor<Class>(filtererList) as IAsyncCursor<Class>;
});
var cursor = await collection.Object.FindAsync(x => x.Id == "2");
var result = await cursor.ToListAsync();
foreach (var item in result)
{
Console.WriteLine($#"Id: {item.Id}, Name: {item.Name}");
}
// Output
// Id: 2, Name: Name2
public class StubAsyncCursor<T> : IAsyncCursor<T>
{
private bool _disposed;
private bool _moved;
private readonly ICollection<T> _current;
public StubAsyncCursor(ICollection<T> current)
{
this._current = current;
}
public IEnumerable<T> Current
{
get
{
this.ThrowIfDisposed();
if (!this._moved)
throw new InvalidOperationException("Must call MoveNextAsync first");
return this._current;
}
}
public bool MoveNext(CancellationToken cancellationToken)
{
this.ThrowIfDisposed();
if (this._moved)
return false;
this._moved = true;
return true;
}
public Task<bool> MoveNextAsync(CancellationToken cancellationToken)
{
this.ThrowIfDisposed();
return Task.FromResult<bool>(this.MoveNext(cancellationToken));
}
public void Dispose()
{
this._disposed = true;
}
private void ThrowIfDisposed()
{
if (this._disposed)
throw new ObjectDisposedException(this.GetType().Name);
}
}
public class Class
{
public string Id { get; set; }
public string Name { get; set; }
}
Even though this is possible, this will only work when using an expression filter definition, I'd personally recommend just spinning up a local MongoDB instance, It's fast, simple and will prove all your queries work as they do in production.
docker run -d -p 27017:27107
I am using Azure search and this is my unit test code:
var expectedResponse = new DocumentSearchResult { };
var _searchIndexRepository = new Mock<ISearchIndexClient>();
_searchIndexRepository
.Setup(r => r.Documents.Search(It.IsAny<string>(), It.IsAny<SearchParameters>(), It.IsAny<SearchRequestOptions>()))
.Returns(expectedResponse);
The error at the time of setup, I get is:
Expression references a method that does not belong to the mocked object
Is there a way to get this working?
Thanks for the suggestions. Below work around solved my problem:
I created a wrapper for SearchIndexClient like this:
public interface ICustomSearchIndexClient
{
DocumentSearchResult<T> Search<T>(string searchTerm, SearchParameters parameters) where T : class;
}
public class CustomSearchIndexClient : ICustomSearchIndexClient
{
private readonly SearchIndexClient _searchIndexClient;
public CustomSearchIndexClient(string searchServiceName, string indexName, string apiKey)
{
_searchIndexClient = new SearchIndexClient(searchServiceName, indexName, new SearchCredentials(apiKey));
}
public DocumentSearchResult<T> Search<T>(string searchTerm, SearchParameters parameters) where T: class
{
return _searchIndexClient.Documents.Search<T>(searchTerm, parameters);
}
}
Changed business logic like this:
Constructor:
public CustomSearchService(string serviceName, string apiKey, string indexName, ICustomSearchIndexClient customSearchIndexClient)
{
_serviceName = serviceName;
_apiKey = apiKey;
_indexName = indexName;
_customSearchIndexClient = customSearchIndexClient;
}
Search method:
public DocumentSearchResult<CustomSearchResult> Search(string search)
{
return _customSearchIndexClient.Search<CustomSearchResult>(string.IsNullOrEmpty(search) ? "*" : search, null)
}
Changed my unit test like this:
[TestCategory("UnitTest")]
[TestMethod]
public void SearchTest()
{
//Arrange
var expectedResponse = new DocumentSearchResult<Models.CustomSearchResult> { Count = 1, Results = <instantiate your custom model here>, Facets = < instantiate your custom model here > };
var searchIndexClient = new Mock<ICustomSearchIndexClient>();
searchIndexClient.Setup(r => r.Search<Models.CustomSearchResult>(It.IsAny<string>(), null)).Returns(expectedResponse);
var business = new CustomSearchService("serviceName", "apiKey", "indexname", searchIndexClient.Object);
//Act
var result = business.Search("search term");
//Assert
Assert.IsNotNull(result, "Business logic method returned NULL");
}
The wrapper ICustomSearchIndex is injected into CustomSearchService business logic using ninject:
Bind<ICustomSearchIndexClient>().To<CustomSearchIndexClient>();
Bind<ICustomSearchService>().To<CustomSearchService>()
.WithConstructorArgument("serviceName", ConfigurationManager.AppSettings["SearchServiceName"])
.WithConstructorArgument("apiKey", ConfigurationManager.AppSettings["SearchServiceApiKey"])
.WithConstructorArgument("indexName", ConfigurationManager.AppSettings["IndexName"]);
I am trying to mock a WriteConcernResult returned via an Insert();
A WriteConcernResult is returned by my provider and the Insert() is called via Add User:
private readonly IMongoProvider _repository;
public UserRepository(IMongoProvider repository)
{
_repository = repository.ForCollection("User");
}
public bool AddUser(UserModel user)
{
var result = _repository.Insert(user);
return result.DocumentsAffected > 0;
}
The MongoDB WriteConcernResult class:
public WriteConcernResult(BsonDocument response)
{
_response = Ensure.IsNotNull(response, nameof(response));
}
public long DocumentsAffected
{
get
{
BsonValue value;
return _response.TryGetValue("n", out value) ? value.ToInt64() : 0;
}
}
Because DocumentsAffected does not have a setter I am unsure how to set a value my mock will return.
{
var user = new UserModel {Name = "Test"};
_mockMongoProvider = MockRepository.GenerateMock<IMongoProvider>();
//mock writeConcernResult
var result = _repo.AddUser(user);
Assert.That(result, Is.True);
}
With a setter I would likely mock like this:
_mockMongoProvider.Stub(p => p.Insert(user))
.Return(new WriteConcernResult(
new BsonDocument()
).DocumentsAffected = 1);
Any guidance would be very helpful, cheers.
Actually you don't need to mock the WriteConcernResult class at all.
All you have to do is to initialize an instance of WriteConcernResult as the following:
var document = new BsonDocument();
document.Add("n", new BsonInt64(1));
var writeConcernResult = new WriteConcernResult(document);
Then you'll have to setup your mock repository to return this instance:
_mockMongoProvider.Stub(p => p.Insert(user))
.Return(writeConcernResult );