I'm trying to create a set of test methods using Moq to cover the external dependencies. These dependencies are async in nature and I have come across a set of them that when awaited they never return, so I'm not sure what I'm missing.
The test itself is very simple.
[TestMethod]
public async Task UpdateItemAsync()
{
var repository = GetRepository();
var result = await repository.UpdateItemAsync("", new object());
Assert.IsNotNull(result);
}
The GetRepository method above is what sets up the various mock objects including called Setup on them.
private static DocumentDbRepository<object> GetRepository()
{
var client = new Mock<IDocumentClient>();
var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
client.Setup(m => m.ReplaceDocumentAsync(It.IsAny<Uri>(), It.IsAny<object>(), It.IsAny<RequestOptions>()))
.Returns(() =>
{
return new Task<ResourceResponse<Document>>(() => new ResourceResponse<Document>());
});
var repository = new DocumentDbRepository<object>(configuration, client.Object);
return repository;
}
The code that is under test is listed below and when the line with the await is executed it never returns.
public async Task<T> UpdateItemAsync(string id, T item)
{
var result = await Client.ReplaceDocumentAsync(UriFactory.CreateDocumentUri(DatabaseId, CollectionId, id), item);
return result.Resource as T;
}
I'm sure that the error is in the Setup method on the Moq object in the GetRepository method, but I'm not sure what the problem is.
You need to fix the set up on the async calls
Moq has a ReturnsAsync that would allow the mocked async method calls to flow to completion.
client
.Setup(_ => _.ReplaceDocumentAsync(It.IsAny<Uri>(), It.IsAny<object>(), It.IsAny<RequestOptions>()))
.ReturnsAsync(new ResourceResponse<Document>());
You typically want to avoid newing up Tasks manually
Related
I am trying to write some unit tests. The application has a number of external API calls that I would like to mock using NSubstitute. The issue is these calls use service objects that need to be instantiated in the function and can't be passed in the constructor after substitution.
For example, in the code below I am adding an account to Quickbooks:
public async Task AddQuickbooksAsync(int accountId)
{
var qbChannel = await GetQuickbooksChannel();
var account = await GetAsync(accountId);
var quickbooksBO = new QuickbooksBO(qbChannel);
quickbooksBO.AddAccount(account);
quickbooksBO.UpdateRefreshedTokens(qbChannel);
await context.SaveChangesAsync();
}
I want to mock the following function call using NSubstitute, but couldn't figure out how:
var quickbooksBO = new QuickbooksBO(qbChannel);
quickbooksBO.AddAccount(account);
How can I do this?
You cannot mock a local variable. You can create a virtual method which will return QuickbooksBO and substitute it.
public async Task AddQuickbooksAsync(int accountId)
{
var qbChannel = await GetQuickbooksChannel();
var account = await GetAsync(accountId);
var quickbooksBO = GetQbBO(qbChannel);
quickbooksBO.AddAccount(account);
quickbooksBO.UpdateRefreshedTokens(qbChannel);
await context.SaveChangesAsync();
}
public virtual QuickbooksBO GetQbBO(Channel qbChannel)
{
return new QuickBooks(qbChannel);
}
After that you can substitute it in your service:
var s = Substitute.ForPartsOf<Service>();
s.GetQbBO(default).ReturnsForAnyArgs(substituteForQbBO);
You will need to figure out what parts of QuickBooksBO you want to mock. Usually such dependencies should be interfaces, without interfaces unit-testing is quite limited.
If QuickbooksBO methods are virtual you can do something like this to get your substituteForQbBO:
var substituteForQbBO = Substitute.ForPartsOf<QuickbooksBO>();
substituteForQbBO.WhenForAnyArgs(x => x.AddAccount(default)).DoNotCallBase();
substituteForQbBO.WhenForAnyArgs(x => x.UpdateRefreshedTokens(default)).DoNotCallBase();
I have a TestMethod async task that is mocking a service setup with ReturnsAsync but for some reason the result keeps returning null.
[TestMethod]
public async Task GetCustomerAsync_Returns_CustomerResults()
{
var customerResults = _fixture.Create<CustomerResults>();
_mockCustomerService.Setup(s => s.GetCustomerAsync(1)).ReturnsAsync(customerResults);
var result = await _customerManager.GetCustomerDetails(1);
Asset.IsNotNull(result);
}
public async Task<CustomerResults> GetCustomerDetails(int id)
{
var results = await _customerService.GetCustomerAsync(id);
return results;
}
You should have posted all the code needed to understand the problem, the creation of _mockCustomerService and _fixture variable, etc.
Having said that i can probably make a guess that you are creating the mockerService and not 'Frezzing' it. The call to _customerManager is not using the _mockCustomerService that you want it to use. You can read about this here AutoFixture Freeze
I'm mocking some implementation using Moq and I want verify a method is called correctly on this interface, the problem is it looks something like this:
public interface IToBeMocked {
void DoThing(IParameter parameter);
}
public interface IParameter {
Task<string> Content { get; }
}
So I set up my mock:
var parameter = "ABC";
var mock = new Mock<IToBeMocked>();
mock
.Setup(m => m.DoThing(It.IsAny<IParameter>()))
.Callback<IParameter>(p async => (await p.Content).Should().Be(parameter));
new Processor(mock.Object).Process(parameter);
mock
.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
Unfortunately, this test already passes with the following implementation:
public class Processor {
public Processor(IToBeMocked toBeMocked){
_toBeMocked = toBeMocked;
}
public void Process(string parameter){
_toBeMocked.DoThing(null);
}
}
Because the Callback is async but the signature requires an Action, meaning the awaiter is never awaited, and the test ends before the exception is thrown.
Is there any functionality in Moq to await an asynchronous callback?
Edit
There seems to be some confusion. I hope this clarifies the problem.
I'm doing TDD. I've implemented the simplest shell of the code to make the test compile. Then I have written the test to ensure "ABC" is the result of the Task and I've set the test running. It's passing. This is the issue. I want the test to fail, so I can implement the 'real' implementation.
Edit 2
The more I think about this the more I think this is probably impossible. I've opened an issue for the repo: https://github.com/moq/moq4/issues/737 but I was thinking about the implementation of such a feature as I was writing the request in anticipation of submitting a PR, and it seems impossible. Still if anyone has any ideas I'd love to hear them either here or in the GitHub issue and I'll keep both places up to date. For now, I suppose I will have to use a stub.
The call back expects an Action, you attempt to perform an async operation in said callback which boils down to async void call. Exceptions cannot be caught in such situations as they are fire and forget.
Reference Async/Await - Best Practices in Asynchronous Programming
So the problem is not with the Moq framework but rather the approach taken.
Use the call back to get the desired parameter and work from there.
Review the progression of the following tests to see how the TDD approach evolves with each test.
[TestClass]
public class MyTestClass {
[Test]
public void _DoThing_Should_Be_Invoked() {
//Arrange
var parameter = "ABC";
var mock = new Mock<IToBeMocked>();
mock
.Setup(m => m.DoThing(It.IsAny<IParameter>()));
//Act
new Processor(mock.Object).Process(parameter);
//Assert
mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
}
[Test]
public void _Parameter_Should_Not_Be_Null() {
//Arrange
IParameter actual = null;
var parameter = "ABC";
var mock = new Mock<IToBeMocked>();
mock
.Setup(m => m.DoThing(It.IsAny<IParameter>()))
.Callback<IParameter>(p => actual = p);
//Act
new Processor(mock.Object).Process(parameter);
//Assert
actual.Should().NotBeNull();
mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
}
[Test]
public async Task _Parameter_Content_Should_Be_Expected() {
//Arrange
IParameter parameter = null;
var expected = "ABC";
var mock = new Mock<IToBeMocked>();
mock
.Setup(m => m.DoThing(It.IsAny<IParameter>()))
.Callback<IParameter>(p => parameter = p);
new Processor(mock.Object).Process(expected);
parameter.Should().NotBeNull();
//Act
var actual = await parameter.Content;
//Assert
actual.Should().Be(expected);
mock.Verify(m => m.DoThing(It.IsAny<IParameter>()), Times.Once);
}
}
It MAY or MAY NOT actually work if you use an async method signature on the callback. Depends if your runtime decides to continue with the same thread or spin up a new one.
If it makes sense, and it often does, to do some Asserts or grab some parameters for later validation in the Callback, you should remove the async stuff all together and force it to run via
Task.Run(() => AsyncMethodHere).Result
I am writing unit tests to test Save DbSet changes. Code looks like:
PreSchoolContext
DbSet<Student> Students{get;set;}
PreSchoolRepository
EDIT: PreSchoolContext is now a IPreSchoolContext instance, handled thru dependency Injection.
public async Task<int> UpdatePreSchoolStudentAsync(Student student)
{
PreSchoolContext.Set<Student>().AddOrUpdate(student);
var result = await PreSchoolContext.SaveChangesAsync().ConfigureAwait(false);
return result;
}
Test Method
//Arrange
var data = GetStudents().AsQueryable();
var mockSet = new Mock<DbSet<Student>>();
mockSet.As<IQueryable<Student>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<Student>(data.Provider));
mockSet.As<IQueryable<Student>>().Setup(m => m.Expression).Returns(data.Expression);
var preSchoolContextMock = new Mock<IPreSchoolContext>();
preSchoolContextMock.Setup(c => c.Students).Returns(mockSet.Object);
var repo = new PreSchoolRepository(preSchoolContextMock.Object);
//Act
var preSchoolStudentUpdateFlag = await repo.UpdatePreSchoolStudentAsync(data.FirstOrDefault());
//Assert
preSchoolStudentUpdateFlag.ShouldNotBeNull();
preSchoolStudentUpdateFlag.ShouldBe(1);
Error
Unable to call public, instance method AddOrUpdate on derived IDbSet type 'Castle.Proxies.DbSet`1Proxy'. Method not found.
Unable to understand what is missing to set the mock data correctly.
Here I Did Some changes to design to test DbSet<> Update functionality:
Added following function under my context class:
public virtual void AddOrUpdateEntity<TEntity>(IProMetrixContext db,
TEntity[] entities) where TEntity : class
{
db.Set<TEntity>().AddOrUpdate(entities);
}
and then slightly changed UpdatePreSchoolStudentAsync(Student student) method as:
public async Task<int> UpdateProMetrixRiskAsync(Student student)
{
PreSchoolContext.AddOrUpdateEntity<Student>(PreSchoolContext, student); //This one resolved issue of unit test and works fine in real scenario too.
var result = await PreSchoolContext.SaveChangesAsync().ConfigureAwait(false);
return result;
}
Got reference here LINK
NOTE: If anyone has suggestions on same, please post.
I have created project where I have repository and query repository. Query() method in every repository return IQueryBuilder to do things.
I have method as below in User class:
public class User
{
//...
public async Task<State> GetCurrentState(IEventRepository entRepository)
{
var lastWorkdayWeekEvents = await eventRepository.Query()
.ByUserId(this.Id).ByDateTimeRange(DateTime.UtcNow.AddDays(-3),
DateTime.UtcNow.AddDays(1)).FilterAutomatic().
ToListAsync();
//return ...
}
}
I would like to test this method. I was thining to use Moq. I would like to mock ToListAsync() method.
[Fact]
public async void GetCurrentState()
{
//arrage
var lastEvents = new Task<List<Event>>(() => new List<Event>
{
new Event{ActivityId = ActivityId.BoxesIn, Address = new Address{Id = 99}}
});
var eventRepository = new Mock<IEventRepository>().Object;
var eventQueryBuilder = new Mock<IEventQueryBuilder>().Setup(x => x.ToListAsync()).Returns(() => lastEvents);
var user = new User();
var result = await user.GetCurrentState(eventRepository);
//...
}
I am getting null reference because eventRepository.Query() returns null. Do I have to mock all return methods from QueryRepository to make it works? How do I make it works without so much work?
I guess methods (ByUserId, ByDateTimeRange, FilterAutomatic, ToListAsync)
are extension methods e.g.
IQuerable ByUserId(this IQuerable events); If it is true you can mock only .Query() method returning something like this:
new List {... you data... }.AsQuerable();
I found solution. Moq provide method SetReturnsDefault(). So the solution is
eventQueryBuilder.SetReturnsDefault(eventQueryBuilder.Object);