Unit testing class instantiation within a method using Moq and C#

Hackered
Wednesday, September 10, 2014
by Sean McAlinden

From a purest perspective, this scenario should never happen as classes should either be injected and treated as class invariants or an injected factory should be used to generate the instance.

Back in the real world however, I have never come across a single codebase without an object being "newed" up within a method or a library such as AutoMapper doing the work for you.

The problem with this is it becomes very hard to unit test (tdd or otherwise... you know who you are).

I quite often see developers pulling out the old It.IsAny<SomeClass>() method to return a value from the mocked service, however the question quickly becomes "what am I actually achieving?".

Luckily Moq has a really nice and easy way to test the internals of a complex parameter when has been instantiated within the method.

The reason for this post is because I have needed to show quite a few people this method recently so it is possibly not so well known.

Lets take the following service:

The Address Service

The address service is a stereotypical C# class utilising the usual culprits:

  • Constructor injection
  • Automapper

I have no doubt you would have seen similar code to this on numerous occasions.

public class AddressService
{
    private readonly IAddressLookupService addressLookupService;

    public AddressService(IAddressLookupService addressLookupService)
    {
        this.addressLookupService = addressLookupService;
    }

    public Address Find(AddressViewModel addressViewModel)
    {
        var addressLookupViewModel = Mapper.Map(addressViewModel); return addressLookupService.Find(addressLookupViewModel); } } 

NOTE: There are a number of ways this class could be improved, however this post is about Moq and testing in-method instantiation.

The Test

So straight to the solution.

There is a little lambda involved but once you have played with it a few times you will get used to it.

Concentrate on the addressLookupService.Setup method.

[Test]
public void Should_Find_And_Return_Address_Successfully()
{
    // Arrange
    const string houseNumber = "1A";
    const string postCode = "SW10 012";
    var expectedAddress = new Address();
    var addressViewModel = new AddressViewModel(houseNumber, postCode);
 
    addressLookupService.Setup(x => x.Find(It.Is<AddressLookupModel>(a =>  
            a.HouseNumber == houseNumber && 
            a.PostCode == postCode)))
            .Returns(expectedAddress);
 
    // Act
    var result = addressService.Find(addressViewModel);
 
    // Assert
    Assert.IsNotNull(result);
    Assert.AreSame(expectedAddress, result);
}

What is going on?

In the addressLookupService.Setup method we are setting up our Find method.

Instead of passing It.IsAny<AddressLookup>() we use the following syntax:

It.Is(a => a.HouseNumber == houseNumber && a.PostCode == postCode)) 

Which is stating that it is an AddressLookupModel and it's property HouseNumber will be equal to our expected value, same deal with the PostCode.

Now due to the wonders of mocking, if the values aren't correct when the AddressLookupModel is passed to the service, the mock parameters would not match therefore a mock is not setup and... boom, your test has failed which is exactly what we want.

Conclusion

If you are writing It.IsAny<SomeClass>() style code, see if you can use It.Is<SomeClass>(s => bool) syntax instead as it provides a lot more value.