Test Driven Development in .NET Part 8: verify class properties with mocks using the Moq framework
April 18, 2013 1 Comment
So far in this series on Moq we’ve been concentrating on mocking methods, their return values and exceptions they may throw. However, we should not forget about object properties: setters and getters. Writing tests may become quite involved when we need to set up a hierarchy of properties: a property of an object is another object which has a property that is also an object which has a property etc.
The demos build on the project where we left off in the previous post.
Setters
The most basic test we can perform is check if a property has been assigned a value, i.e. the property setter has been called. We’ll add a property to ICustomerRepository called LocalTimeZone and see if that setter has been called when creating a new Customer.
Open the solution we’ve been working with in this series on Moq. Add a new class called When_creating_a_customer to the CustomerService_Tests folder and insert the following test skeleton:
[TestFixture] public class When_creating_a_customer { [Test] public void The_local_timezone_should_be_set() { //Arrange Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>(); Mock<ICustomerStatusFactory> mockCustomerStatusFactory = new Mock<ICustomerStatusFactory>(); CustomerViewModel customerViewModel = new CustomerViewModel() { FirstName = "Elvis" , LastName = "Presley" , Status = CustomerStatus.Gold }; CustomerService customerService = new CustomerService(mockCustomerRepository.Object, mockCustomerStatusFactory.Object); //Act customerService.Create(customerViewModel); } }
This should be familiar from the discussions in the previous post. What we’re missing is the Assert section:
//Assert mockCustomerRepository.VerifySet(c => c.LocalTimeZone = It.IsAny<string>());
VerifySet means that we want to verify that a setter has been called. We specify which setter we mean by the lambda expression. Let VS create the property stub for LocalTimeZone. The new test should fail as we do not set this property in the Create method. Let’s modify it now:
public void Create(CustomerViewModel customerToCreate) { var customer = new Customer( customerToCreate.FirstName, customerToCreate.LastName); customer.StatusLevel = _customerStatusFactory.CreateFrom(customerToCreate); _customerRepository.LocalTimeZone = TimeZone.CurrentTimeZone.StandardName; if (customer.StatusLevel == CustomerStatus.Gold) { _customerRepository.SaveSpecial(customer); } else { _customerRepository.Save(customer); } }
The test should now pass along with all the other tests.
Getters
We want to be able to update a customer. The Customer object will receive a new property called RegionId which will be set using a getter of the ICustomerRepository interface. We want to make sure that this getter has a value, i.e. is not null.
Add a new class to CustomerService_Tests called When_updating_a_customer with the following starting point for a test:
[TestFixture] public class When_updating_a_customer { [Test] public void The_region_id_of_repository_should_be_used() { //Arrange Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>(); Mock<ICustomerStatusFactory> mockCustomerStatusFactory = new Mock<ICustomerStatusFactory>(); CustomerViewModel customerViewModel = new CustomerViewModel() { FirstName = "Elvis" ,LastName = "Presley" ,Status = CustomerStatus.Gold }; CustomerService customerService = new CustomerService(mockCustomerRepository.Object, mockCustomerStatusFactory.Object); //Act customerService.Update(customerViewModel); } }
Note the addition of the Update method, let VS create it for you.
Add the following setup statement just before the construction of the CustomerService object:
mockCustomerRepository.Setup(c => c.RegionId).Returns(123);
This looks very much like setting up the return value of a method. Let VS take care of the RegionId property. Make sure the generated property is of type nullable integer. We’re missing the Assert statement:
//Assert mockCustomerRepository.VerifyGet(x => x.RegionId);
VerifyGet – as you may have guessed – means that we want to make sure that a getter was called. The getter is then defined by the lambda expression. Run the test and as usual the new one should fail. Let’s implement the Update method:
public void Update(CustomerViewModel customerViewModel) { var customer = new Customer( customerViewModel.FirstName, customerViewModel.LastName); int? regionId = _customerRepository.RegionId; if (!regionId.HasValue) { throw new InvalidRegionIdException(); } customer.RegionId = regionId.Value; _customerRepository.Update(customer); }
As usual, let VS take care of the missing elements. The test should now pass.
Mocking property hierarchies
One extra scenario we must consider is when a dependency returns a deeply nested property and we want to access the end of the nested property chain. Example: the Settings property of a Customer is returned as follows:
customer.Settings = _customerRepository.SystemConfig.BaseInformation.Settings;
This is exactly what we want to achieve. Open When_creating_a_customer.cs and insert a new test method called The_customer_settings_should_be_retrieved(). Arrange and Act will be familiar:
//Arrange Mock<ICustomerRepository> mockCustomerRepository = new Mock<ICustomerRepository>(); Mock<ICustomerStatusFactory> mockCustomerStatusFactory = new Mock<ICustomerStatusFactory>(); CustomerViewModel customerViewModel = new CustomerViewModel() { FirstName = "Elvis" ,LastName = "Presley" ,Status = CustomerStatus.Gold }; CustomerService customerService = new CustomerService(mockCustomerRepository.Object, mockCustomerStatusFactory.Object); //Act customerService.Create(customerViewModel);
We only need to include the Assert phase now. We want to assert that the Settings property has been retrieved:
mockCustomerRepository.VerifyGet(x => x.SystemConfig.BaseInformation.Settings);
Create the necessary classes and properties. Don’t worry about the implementations. Run the tests and the new test will fail as usual. Go to CustomerService.Create and include the following call just below _customerStatusFactory.CreateFrom:
customer.Settings = _customerRepository.SystemConfig.BaseInformation.Settings;
Run the tests and 3 of them should fail:
We forgot to properly set up the expectations on the nested properties so they all return null causing a null reference exception to be thrown. Include the following Setup in the Arrange section:
mockCustomerRepository .Setup(x => x.SystemConfig.BaseInformation.Settings) .Returns(new Settings());
Run the tests again and you’ll see that we still have the previous 3 failing methods. However, the error message on the test we’re currently working on is different: invalid setup on a non-virtual member. It turns out that this is due to Moq, not our code really. Moq can make it easy to mock up a chain of nested properties like that but it needs some help. The solution is to mark the properties in that chain with the ‘virtual’ keyword to let Moq know it is a mockable property:
public class SystemConfig { public virtual BaseInformation BaseInformation { get; set; } } public class BaseInformation { public virtual Settings Settings { get; set; } } public class Settings { public virtual string Colour { get; set; } }
Another solution would be to turn BaseInformation, Settings and SystemConfig properties to interfaces, but I think in this case it would be wrong. They are object properties, not interfaces.
This is the trade-off. In other mocking frameworks you may need to set up the expectations on each nest property one by one. Moq makes our lives easier by mocking up each property in the chain for us, but in turn we need to add the ‘virtual’ modifier. Run the tests and the current one should pass. Add the same setup to the other two failing tests as well.
Stubbing properties
There are occasions where a dependency has many properties that need to be set up. It is tedious to call a Setup on each. Instead we can instruct Moq to create a stub of the dependency as opposed to a mock. Stubbing is performed via the SetupAllProperties method:
mockCustomerRepository.SetupAllProperties(); mockCustomerRepository.Object.SystemConfig = new SystemConfig();
After calling SetupAllProperties we can set the value of each property as shown in the example.
In the next post we’ll investigate some more advanced features of this mocking framework.
Hi, Andras,
thank you for your grate articles about NUnit, very usefull for me!
Лариса.