Moq : Setup, Verify, Verifiable & Callback explained

Consider the following method which needs to be tested.

public virtual IncomePeriod AddIncomePeriod(IncomePeriod incomePeriod)
{

if(incomePeriod.StartDate > incomePeriod.EndDate) throw new ValidationException("Start date cannot be greater than end date.", "Start date cannot be greater than end date.");

return _repository.AddIncomePeriod(incomePeriod);
}

and one of the unit test of this method

[TestMethod]
public void AddIncomePeriod_ShouldCallRepository_WithCorrectIncomePeriod()

{
var newIncomePeriod = new IncomePeriod
{

Id = 3333,

StartDate = new DateTime(2019, 9, 1),

EndDate = new DateTime(2019, 9, 30),

Primary = 10000,

Additional = 500,

Name = "September",

User = new User

{

Id = testUserId1

}

};

mockRepository.Setup(x =>; x.GetIncomePeriods()).Returns(mockIncomePeriods.AsQueryable());

mockRepository.Setup(x =>; x.AddIncomePeriod(It.IsAny<IncomePeriod>())).Returns<IncomePeriod>(x => x).Verifiable();

var service = new IncomePeriodService(mockRepository.Object);

var result = service.AddIncomePeriod(newIncomePeriod);

mockRepository.Verify(x => x.AddIncomePeriod(newIncomePeriod));

}

mockRepository.Setup(x => x.AddIncomePeriod(It.IsAny<IncomePeriod>())).Returns<IncomePeriod>(x => x).Verifiable();

  • ‘Setup’ mocks a method and ‘Returns’ specify what the mocked method should return.
  • ‘Verifiable’ marks this expectation to verified at the end when Verify or VerifyAll is called i.e. whether AddIncomePeriod was called with an object of IncomePeriod and if it returned the same output.

mockRepository.Verify(x => x.AddIncomePeriod(newIncomePeriod));

  • Ensures that AddIncomePeriod is called once with exact object newIncomePeriod
  • Only the reference is checked not the individual values i.e. even if you change the values of some properties e.g. newIncomePeriod.Additional = 100 it would still come out as correct

mockRepository.VerifyAll()

  • Verifies all the expectations on the mocks. E.g. in the above example mockRepository.Setup(x => x.GetIncomePeriods()).Returns(mockIncomePeriods.AsQueryable()); will throw error as GetIncomePeriods was never called

mockRepository.Verify();

  • Verifies only the expectations marked as verifiable e.g. in this case on mockRepository.Setup(x => x.AddIncomePeriod(It.IsAny<IncomePeriod>())).Returns<IncomePeriod>(x => x).Verifiable(); will be verified i.e. It should take an object of income period and should return the same object.

To understand ‘callback’ take a look at another test method below and scenario it is testing.

[TestMethod]
public void AddBudget_WhenBudgetForAllCategoriesIsNotDefined_ThenAddRemainingCategoriesForUserWithZeroBudget()
{

mockRepository.Setup(x => x.GetCategories()).Returns(mockCategories.AsQueryable()).Verifiable();
Budget modifiedBudget = new Budget();
mockRepository.Setup(x => x.SaveBudget(testBudget)).Callback<Budget>(x =>
{
modifiedBudget = x;
});
var budgetService = new BudgetService(mockRepository.Object);
budgetService.SaveBudget(testBudget);
modifiedBudget.Items.Count.ShouldBe(mockCategories.Where(x=>x.User.Id == user1.Id).Count());
modifiedBudget.Items.Any(x => x.ExpenseCategoryId == 1111).ShouldBeTrue();
modifiedBudget.Items.Any(x => x.ExpenseCategoryId == 2222).ShouldBeTrue();
mockRepository.Verify();
}

Basically expectation is that while saving a budget we should have budget for all the expense categories and in case the user has not given budget for all the categories system before saving should add rest of the categories with zero amount and save.

mockRepository.Setup(x => x.SaveBudget(testBudget)).Callback<Budget>(x =>
{
modifiedBudget = x;
});

  • Here we define a delegate in callback which will be called when SaveBudget method on the repository is called and it enables us to investigate the ‘testbudget’ object (by assigning it to local variable ‘modifiedBudget’ and then asserting on it ) and see if all the additional categories have been initialized as expected by the test.

To get more details to refer this pluralsight course on mocking .net core unit tests with moq and another one on mocking with NUnit and Moq.

 

Tagged on: , ,

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.