Background

Sometimes, testing software is hard. Long term, having tests covering your line of business code makes your software better and more maintainable. Unfortunately, when testing causes friction, it usually will not get done. After all, testing is supposed to reduce friction, not add it to your process.

Setting the Stage

A concrete example that I received from a friend of mine has many levels of nested interfaces.  The example is shown in Listing 1.

public interface IInstallInfo
{
    IList<IInstallPackage>InstallPackages { get; set; }
}
public interface IInstallPackage
{
    IInstallerInfo Installer { get; set; }
}
 
public interface IInstallerInfo
{
 string Name { get; set; }
 
    IDetectionInfoBase BlockingCondition { get; set; }
}
 
public interface IDetectionInfoBase
{
 string Name { get; set; }
}

Listing 1 – Many levels of nested interfaces

JustMock has supported nested interfaces for a long period of time in a somewhat limited manner.  In previous versions, you would first create a mock for the top level interface (in this case the IInstallInfo interface), and then you could arrange the behavior of the tail end of the nesting.  JustMock will create implicit mocks for the dependencies in between the head and the tail.  In most cases this works just fine.  However, if you need to arrange the behavior of the dependencies in between the head and the tail, you need to do more.

Mocking the Old Way

If you wanted to create arrangements on the intermediate mocks, you would have to explicitly create the mocks from the head to the tail.  In this particular example, the mocks are very similar, and this leads to a serious of clipboard inherited lines of code.  An Example is shown in Listing 2.

[Test]
public void ShouldCreateMocksTheOldWay()
{
    var blockingCondition1 = Mock.Create<IDetectionInfoBase>();
    Mock.Arrange(() => blockingCondition1.Name).Returns("foo");
 
    var installer1 = Mock.Create<IInstallerInfo>();
    Mock.Arrange(() => installer1.BlockingCondition).Returns(blockingCondition1);
    Mock.Arrange(() => installer1.Name).Returns("blocked1");
 
    var package1 = Mock.Create<IInstallPackage>();
    Mock.Arrange(() => package1.Installer).Returns(installer1);
 
    var blockingCondition2 = Mock.Create<IDetectionInfoBase>();
    Mock.Arrange(() => blockingCondition2.Name).Returns("bar");
 
    var installer2 = Mock.Create<IInstallerInfo>();
    Mock.Arrange(() => installer2.BlockingCondition).Returns(blockingCondition2);
    Mock.Arrange(() => installer2.Name).Returns("blocked2");
 
    var package2 = Mock.Create<IInstallPackage>();
    Mock.Arrange(() => package2.Installer).Returns(installer2);
 
    var installInfo = Mock.Create<IInstallInfo>();
    Mock.Arrange(() => installInfo.InstallPackages)
        .Returns(new List<IInstallPackage> { package1, package2 });
}
 

Listing 2 – Explicitly creating multiple mocks

A Better Way to Mock

The Act and Assert parts of the test may be a single line each, but sometimes the Arrange part can be really tiresome to set up. Notably, the code is full of temporary variables that serve no other purpose but to link the arrange statements, and of endless repetitions of Mock.Arrange(() => ...).Returns(...);

Since the Q2 2013 release, arranging these dependencies with JustMock is a whole lot simpler. Listing 3 shows the same arrangement as Listing 2, but using Mock by Example syntax.

[Test]
public void ShouldMockByExample()
{
    var installInfo = Mock.CreateLike<IInstallInfo>(me =>
        me.InstallPackages == new List<IInstallPackage>
        {
            Mock.CreateLike<IInstallPackage>(
               pkg => pkg.Installer.Name == "blocked1"
                   && pkg.Installer.BlockingCondition.Name == "foo"),
            Mock.CreateLike<IInstallPackage>(
               pkg => pkg.Installer.Name == "blocked2"
                   && pkg.Installer.BlockingCondition.Name == "bar"),
        }
    );
}
 

Listing 3 – Mocking By Example

The new syntax reflects the hierarchical structure of the complex arrangement much better and makes trivial arrangements (like for the values of properties) easy.

Create Like

The first you will notice is the “CreateLike” call to create the outer mock object.  Why the new name?  Well, the simple reason is that Create and the necessary overloads are already in use. Smile  Actually, it very well describes the feature.  Let’s look at the first statement, as shown in Listing 4.

var installInfo = Mock.CreateLike<IInstallInfo>(me =>
    me.InstallPackages == new List<IInstallPackage>
    {
    }
);

Listing 4 – Creating the IInstallInfo

This statement creates a proxy around IInstallInfo with the behavior specified in the lambda, specifically that any calls to the InstallPackages property will return a list of IInstallPackages.  Think of this as “Create a mock that behaves Like…”

Combining Creation and Arrangement

Notice that there aren’t any specific calls to Arrange in the statement in Listing 4.  This is handle by the combination of the CreateLike statements and the lambda. The single line of code is equivalent to calling the code shown in Listing 5.

var installInfo = Mock.Create<IInstallInfo>();
Mock.Arrange(() => installInfo.InstallPackages)
  .Returns(new List<IInstallPackage> {  });
 

Listing 5 – Explicitly creating the mock and arranging a property

Combining Mocking By Example with Recursive Mocking

In this particular example, not every dependency in the tree needs to be explicitly mocked.  We can leverage recursive mocking where we can to make the code even cleaner.  We need explicit mocks for the IInstallPackage interfaces, but we can use implicit mocks for the IInstallerInfo and IDetectionInfoBase interfaces.  We can also “and” multiple arrangements in the same call to CreateLike.  In the code in Listing 6, I create a proxy for the IInstallPackage, then arrange that any calls to Installer.Name returns “blocked1” and any calls to Installer.BlockingCondition.Name returns “foo”.  Neither the Installer property (IInstallerInfo) of the IInstallPackage proxy nor the BlockingCondition property (IDetectionInfoBase) of the Installer need to be explicitly mocked.

Mock.CreateLike<IInstallPackage>(
    pkg => pkg.Installer.Name == "blocked1" &&
           pkg.Installer.BlockingCondition.Name == "foo"),
 

Listing 6 – Combining CreateLike and Recursive Mocking

Additional Features

The new syntax fully supports using argument matchers in method arguments, just like in regular Mock.Arrange() expressions. An example of this is shown in Listing 7.

[Test]
public void ShouldMockByExample()
{
    var sut = Mock.CreateLike<IInstallPackage>(
        me => me.Installer.GetName(Arg.AnyString) == "foo");            
}

Listing 7 – Using Argument Matchers with CreateLike

You can even refer to the arguments of the method when specifying its return value, as shown in Listing 8.

Mock.CreateLike<IEqualityComparer>(
   cmp => cmp.Equals(Arg.AnyObject, Arg.AnyObject) 
        == Object.Equals(Param._1, Param._2)); 
 

Listing 8 – Using

In the above example the Param._1 and Param._2 bits are placeholders for the actual arguments passed to the arranged method. Here it means: call the Object.Equals method with the first and second arguments passed to cmp.Equals(…).

Additional Notes

Additional notes, pitfalls and tricks:

· The arranged expression must always be on the left side of the == operator and the implementation – on the right.

· The feature is available in both JustMock and JustMock Lite (JML). In JustMock, with the profiler enabled, you can create arrangements that require elevated mocking.

· If the type of a parameter is not one of the supported types (most commonly occurring for non-generic types, like int, string, etc.), specify the type of the parameter explicitly, e.g. Param<IEnumerable<int>>._1

· You can arrange boolean members implicitly – instead of writing “&& me.Bool == true”, simply write “&& me.Bool”; also, instead of writing “&& me.Bool == false”, write “&& !me.Bool”.

· You cannot set expectations on arrangements – only the return values. If you need to set an expectation, then make an additional arrangement on the created mock for both its return value and any expectations using the old Mock.Arrange() syntax.

· At any time, for any mock, you can add additional arrangements using the new syntax by calling Mock.ArrangeLike(someMock, me => ...);

Summary

It might take a while to wrap one’s mind around the new syntax, but once that is over with, you’ll notice a boost in the speed with which you produce new tests ;)

JustCode download banner image


Japikse
About the Author

Phil Japikse

is an international speaker, a Microsoft MVP, ASPInsider, INETA Community Champion, MCSD, CSM/ CSP, and a passionate member of the developer community. Phil has been working with .Net since the first betas, developing software for over 20 years, and heavily involved in the agile community since 2005. Phil also hosts the Hallway Conversations podcast (www.hallwayconversations.com) and serves as the Lead Director for the Cincinnati .Net User’s Group (http://www.cinnug.org). You can follow Phil on twitter via www.twitter.com/skimedic, or read his personal blog at www.skimedic.com/blog.

 

Comments

Comments are disabled in preview mode.