Telerik blogs

Once again, the brilliant engineers from Telerik’s Mocking and Tracing Team have packed some amazing features into JustMock for the newest release.  The new features and updates include integration with JustCode to speed writing your tests, improved workflow, deep looks under the cover of mocking in unit tests with the Introspection API, as well as recursive loose mocking.

JustCode Integration

Creating and Arranging Mocks

The combination of JustCode and JustMock is now even more compelling.  Leveraging the  power of JustCode’s integration with Visual Studio allows quick creation and arrangement of you mock objects.  Take the following code snippet:

[TestFixture]
public class JustMockTests
{
    [Test]
 public void ShouldIntegrateWithJustCode()
    {
        IRepository repo;
    }
}
 
public interface IRepository
{
    IList<string> GetStudentNames();
}

Listing 1 – Start of a Unit Test

I’ve started to arrange the test, but I still need to create the mock as well as arrange it.  Simple!  With the cursor on the variable name “repo”, click CTL-~ to bring up the JustCode Visual Aids and select Code –> Create Mock as shown in Figure 1. Alternatively, right click and select Just Code->Create Mock.

image
Figure 1 – Using the JustCode Visual Aid to create the mock.

The resulting code is shown in Listing 2.

[Test]
public void ShouldIntegrateWithJustCode()
{
    IRepository repo = Mock.Create<IRepository>();
}

Listing 2 – The Created Mock

Arranging the mock is just as easy!  Before arranging the mock, let’s update the interface to add some additional methods so we can see the full power of the integration.  The updated code is shown in Listing 3.

public interface IRepository
{
    IList<string> GetStudentNames();
    IList<string> GetStudentNames(string param);
    IList<string> GetStudentNames(int count);
}

Listing 3 – The updated interface

Bring up the Visual Aid, select Code, and Arrange Mock as in Figure 2.

image
Figure 2 – Arranging the mock with the Visual Aid

The Arrange Mock integration uses JustCode’s static analysis to show all of the available properties and methods (even private methods) on the mock. For our interface that has three methods, the Arrange Mock window looks like Figure 3.

image
Figure 3 – The Arrange Mock Integration

The top check box (indicated with the Number 1 in the figure) provides a quick check for selecting or deselecting all of the methods available. The middle area shows all of the methods available to be arranged.  The last number determines whether the classic syntax or the fluent syntax is used.

For this example, I’m going to select all of the members. The resulting code is shown in Listing NOTE: In a typical unit test you probably wouldn’t select all of the members of a mock, but quite often I find myself needing more than one, so this is a great time saver.

After arranging the mock (using the classic syntax) we get the code shown in Listing 4. 

[Test]
public void ShouldIntegrateWithJustCode()
{
    IRepository repo = Mock.Create<IRepository>();
    Mock.Arrange(() => repo.GetStudentNames()).Returns(default(IList<string>));
    Mock.Arrange(() => repo.GetStudentNames(Arg.AnyString)).Returns(default(IList<string>));
    Mock.Arrange(() => repo.GetStudentNames(Arg.AnyInt)).Returns(default(IList<string>));
}

Listing 4 – Arranged Mock using the classic syntax

If you select the fluent syntax, you will see the code shown in Listing 5

[Test]
public void ShouldIntegrateWithJustCode()
{
    IRepository repo = Mock.Create<IRepository>();
    repo.Arrange(x => x.GetStudentNames()).Returns(default(IList<string>));
    repo.Arrange(x => x.GetStudentNames(Arg.AnyString)).Returns(default(IList<string>));
    repo.Arrange(x => x.GetStudentNames(Arg.AnyInt)).Returns(default(IList<string>));
}

Listing 5 – Arranged Mock using the fluent syntax

Improved Workflow

Tests Fail.  It’s a fact of life.  When a test fails, you want to quickly figure out what happened.  With the new improved workflow and error messages,all of the information that you need is at your fingertips.

Take the code in Listing 6.  The test fails because the arranged methods are called out of order.  In prior versions of JustMock, if you forgot to call Assert at the end of the test, the failing call order wouldn’t be reported.  Now, with 2013 Q3, it fails the test without needing the Assert. 

[TestFixture]
public class Diagnostics
{
    [Test]
 public void ShouldHaveBetterWorkFlow()
    {
 string userName = "Bob";
 string password = "password";
 string message = "Invalid log in attempted";
        var mockValidator = Mock.Create<IUserValidation>();
        mockValidator.Arrange(x => x.ValidateUser(userName, password)).Returns(false).InOrder();
        var mockLogger = Mock.Create<ILogging>();
        mockLogger.Arrange(x => x.LogMessage(userName, message)).InOrder();
        var sut = new Login(mockValidator,mockLogger);
        var result = sut.LoginUser(userName, password);
    }
}
public class Login
{
 private IUserValidation _mockValidator;
 private ILogging _mockLogger;
 public Login(IUserValidation mockValidator, ILogging mockLogger)
    {
 this._mockValidator = mockValidator;
 this._mockLogger = mockLogger;
    }
 public bool LoginUser(string userName, string password)
    {
        _mockLogger.LogMessage(userName, "Invalid log in attempted");
        var result = _mockValidator.ValidateUser(userName, password);
 return result;
    }
}
public interface IUserValidation
{
 bool ValidateUser(string userName, string password);
}
public interface ILogging
{
 void LogMessage(string userName, string message);
}

Listing 6 – Intentionally failing test

When you run this test with the JustCode Test Runner, you get the results shown in Listing 7.

Error Message:
Last call executed out of order. Order of calls so far:
JustMockWebinar.ILogging.LogMessage(Bob, Invalid log in attempted) called at:
    at JustMockWebinar.Login.LoginUser(String userName, String password) in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 39
<rest of stack trace removed for brevity>
 
Exception Message:
Last call executed out of order. Order of calls so far:
JustMockWebinar.ILogging.LogMessage(Bob, Invalid log in attempted) called at:
    at JustMockWebinar.Login.LoginUser(String userName, String password) in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 39
<rest of stack trace removed for brevity>
 
Stacktrace:
<rest of stack trace removed for brevity>
  at JustMockWebinar.Login.LoginUser(String userName, String password) in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 39
  at JustMockWebinar.Diagnostics.ShouldHaveBetterWorkFlow() in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 22
 
    at Telerik.JustCode.TestRunner.NUnitV26.Runner.NUnitRunner.#Cce(String #jge, ITestFilter #ie) in c:\_J\workspace\Installer-release\src\TestRunner.NUnitV26.Runner\NUnitRunner.cs:line 165
    at Telerik.JustCode.TestRunner.NUnitV26.Runner.NUnitRunner.RunTestTask(UnitTestRunTask testRunTask) in c:\_J\workspace\Installer-release\src\TestRunner.NUnitV26.Runner\NUnitRunner.cs:line 130
 
<rest of stack trace removed for brevity>
  at JustMockWebinar.Login.LoginUser(String userName, String password) in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 39
  at JustMockWebinar.Diagnostics.ShouldHaveBetterWorkFlow() in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 22
Listing 7 – Error Details (edited for brevity)

If we change the Login class and the test to match what’s in Listing 8, we see that the Occurs(0) on the logger will create a failing test.

    [Test]
 public void ShouldHaveBetterWorkFlow()
    {
 string userName = "Bob";
 string password = "password";
 string message = "Invalid log in attempted";
        var mockValidator = Mock.Create<IUserValidation>();
        mockValidator.Arrange(x => x.ValidateUser(userName, password)).Returns(false).InOrder();
        var mockLogger = Mock.Create<ILogging>();
        mockLogger.Arrange(x => x.LogMessage(userName, message)).InOrder().Occurs(0);
        var sut = new Login(mockValidator,mockLogger);
        var result = sut.LoginUser(userName, password);
    }
}
public class Login
{
 private IUserValidation _mockValidator;
 private ILogging _mockLogger;
 public Login(IUserValidation mockValidator, ILogging mockLogger)
    {
 this._mockValidator = mockValidator;
 this._mockLogger = mockLogger;
    }
 public bool LoginUser(string userName, string password)
    {
        var result = _mockValidator.ValidateUser(userName, password);
 if (!result)
        {
            _mockLogger.LogMessage(userName, "Invalid log in attempted");
        }
 return result;
    }
}
 

Listing 8 – Updated Test and Login class to check for occurrences

When we running the test, it fails on the Occurs(0) immediately, and gives us the following error message (edited for brevity) shown in Listing 9.

Error Message:
Occurrence expectation failed. Expected no calls. Calls so far: 1
Stacktrace:
<stack trace removed for brevity>
  at JustMockWebinar.Diagnostics.ShouldHaveBetterWorkFlow() in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 22
 

Listing 9 – Error message from failed upper bounds

Failing a test immediately for upper bounds violations (getting called too many times compared to the arrangement) makes perfect sense.  What about lower bounds failures?  Failing the test immediately does not make sense because you really don’t know how many times something will be called until the test ends.

For lower bounds violations, we need to have the Assert() back in the test. If we change the Occurs(0) to Occurs(2) and run the test as it is, we don’t get the expected failure.  JustMock (and the test framework) need a signal that the test is over and it’s time to verify all of the arrangements.

So if we change the test to the code shown in Listing 10, we see the error message shown in Listing 11.

[Test]
public void ShouldHaveBetterWorkFlow()
{
 string userName = "Bob";
 string password = "password";
 string message = "Invalid log in attempted";
    var mockValidator = Mock.Create<IUserValidation>();
    mockValidator.Arrange(x => x.ValidateUser(userName, password)).Returns(false).InOrder();
    var mockLogger = Mock.Create<ILogging>();
    mockLogger.Arrange(x => x.LogMessage(userName, message)).InOrder().Occurs(2);
    var sut = new Login(mockValidator,mockLogger);
    var result = sut.LoginUser(userName, password);
    mockLogger.Assert();
    mockLogger.Assert();
}

Listing 10 – Updated Test

Error Message:
Occurrence expectation failed. Expected exactly 2 calls. Calls so far: 1
Arrange expression: x => x.LogMessage(userName, message)
Stacktrace:
<stack trace removed for brecity>
  at JustMockWebinar.Diagnostics.ShouldHaveBetterWorkFlow() in c:\Users\Japikse\SkyDrive\Speaking-Presentations\UnitTesting\2013-Q3-JustMockWebinar\JustMockWebinar\JustMockWebinar\Diagnostics.cs:line 23
 
Listing 11 – Improved error messages for Assertion failures.

Introspection API

The Introspection API opens up JustMock for a deep look at what is going on under the hood.  To use this, add a Watch for Telerik.JustMock.DebugView.  Using the same test in Listing 6, set a break point on the Assert.  When you debug the test and expand the watch variable, you see the details for the DebugView as in Figure 4.

image
Figure 4 – Telerik.JustMock.DebugView as a watch variable

To the far right of the CurrentState (cut off in the image by the blog width) is the visualizer icon.  When you click on that, you get the image shown in Figure 5 (your values will probably be different).

image
Figure 5 – Details of the CurrentState

Instrumentation of DLLImport Methods

JustMock is the first mocking framework to instrument DLLImport methods, allowing you to very quickly and easily mock C# calls to unmanaged code. Take the code in Listing 12.

[TestFixture]
public class DLLImport
{
    [Test]
 public void ShouldMockDLLImport()
    {
 int expected = 10;
        Mock.Arrange(() => MyImportedMethod.GetCurrentProcessId()).Returns(expected);
        var actual = MyImportedMethod.GetCurrentProcessId();
        Assert.AreEqual(actual, expected);
    }
}
public class MyImportedMethod
{
    [DllImport("Kernel32.dll")]
 public static extern int GetCurrentProcessId();
}
Listing 12 – Testing Unmanaged code

Running this test passes, and shows how easy it is to abstract dependencies away from your test, even those that are unmanaged.

NOTE: This is only available in the commercial version of JustMock.

Recursive Loose Mocks

Suppose you have a dependency chain such as shown in Listing 13. 

public interface ITopLevel
{
    INextLevel NextLevel { get; set; }
}
public interface INextLevel
{
 int DoSomething();
    IMiddleLevel MiddleLevel { get; set; }
}
public interface IMiddleLevel
{
    IBottomLevel BottomLevel { get; set; }
}
public interface IBottomLevel
{
 bool DoIt();
}

Listing 13 – Nested Dependency Chain

The class that uses the dependency chain is shown in Listing 14.

public class ComplexClass
{
 private ITopLevel _service;
 public int UserID { get; private set; }
 public ComplexClass(ITopLevel service)
    {
 this._service = service;
    }
 public bool UseTheComplexity()
    {
        UserID = _service.NextLevel.DoSomething();
 return _service.NextLevel.MiddleLevel.BottomLevel.DoIt();
    }
}

Listing 14 – Class that uses the nested dependency chain

Recursive mocking provides the mechanism so that only the top level interface needs to be mocked, and by arranging the end method, the entire dependency chain get’s mocked.  this capability has been in JustMock for quite a while now.  What’s new this quarter is the default behavior has been change to have recursive loose mocks created all the way down.  This means that every mock in the dependency chain has a proxy built for it, which eliminates the null reference error that could have been encountered with recursive mocks in the past.

Look at the test in Listing 15.  The mock is created for ITopLevel, and nothing is arranged.  A loose mock will return the default value for the method’s return type when not arranged (whereas a strict mock or an object that doesn’t have a proxy will cause an error).  When the complexity is used in the system under test, we simply get returned a zero instead of an exception. 

[Test]
public void ShouldCreateMocksAllTheWayDownWithoutArrange()
{
    ITopLevel topLevel = Mock.Create<ITopLevel>();
    var sut = new ComplexClass(topLevel);
    var result = sut.UseTheComplexity();
    Assert.IsFalse(result);
    Assert.AreEqual(0, sut.UserID);
}

Listing 15 – Creating a recursive loose mock

Summary

The new features add a lot of additional capabilities into an already powerful mocking framework.  Go ahead.  Give it a spin.  I’m pretty sure you’re going to like what you see!

For more information on JustMock, check out our Pluralsight course on JustMock Lite Fundamentals.

JustMock banner

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.

 

Related Posts

Comments

Comments are disabled in preview mode.