Sunday, November 23, 2014

Moq and Out Parameters or Parameters Passed by Reference

This post continues with my previous adventures with TDD. This time I was doing some ASP.Net MVC coding and wanted to make some unit tests for my controller. The controller used a “Logic” type class to do the database interaction and some logic before returning a result to the controller. The issue discussed in this post came about because the logic class would change the value of an out parameter passed into it. This parameter would then be checked to see if it was still null and wouldn’t go down a happy path if it was, and I was trying to test out the happy path. So I needed the mocked logic class to change the value of the out parameter to an expected value before continuing. In another case, I had a sql server stored procedure that included a return value parameter, and the calling method would then act on that return value parameter value. Since the return value parameter wouldn’t be set in the mocked up database layer, when the calling code did the check on the value, it would throw a “NullReferenceException”, but only in the unit test. In essence, I needed a way to change the value of a passed in parameter of a mocked method if the parameter was an “out” parameter, or if it was passed in by reference, either implicitly, or with the “ref” keyword. Finding the solution and clear examples explaining was difficult, and there seems to be some gaps in the Moq framework which adds confusion to the subject. Here is my attempt to clarify. For each of the examples below I use the following classes:
 //This is the interface for the logic class.  Using an interface allows Moq to mock up the class. 
    public interface ILogicClass
    {
        void MethodWithObjectParameter(ContainerClass containerClass);
        void MethodWithOutParameter(out int myValue);
        void MethodWithRefParameter(ref int myValue);
        void MethodWithRefObjectParameter(ref ContainerClass containerClass);
    }

    //This is a very simplified version of a logic class.  In each method, a parameter is taken in and it's value is changed to '42'. 
    //All of the methods return void.  Although in this instance it'd be really easy to just use this class in the unit tests (since it's just code and has 
    //no other dependencies like a database or flie or something), we're going to use the interface above and pretend like it has offline dependencies. 
    public class LogicClass : ILogicClass
    {
        //This method takes in an out parameter.  
        public void MethodWithOutParameter(out int myOutValue)
        {
            myOutValue = 42;
        }

        //The parameter of this method is an instance of the ContainerClass.  It is passed implicitly by reference.
        public void MethodWithObjectParameter(ContainerClass containerClass)
        {
            containerClass.MyProperty = 42;
        }

        //The parameter of this method is an instance of the ContainerClass.  It is passed explicitly by reference.  
        public void MethodWithRefObjectParameter(ref ContainerClass containerClass)
        {
            containerClass.MyProperty = 42;
        }

        //This method takes in a primitive int by using the "ref" keyword.  
        public void MethodWithRefParameter(ref int myRefValue)
        {
            myRefValue = 42;
        }
    }

    //The TestedClass is the class that I'm acutally writing up the unit tests for, as compared to unit tests created to 
    //test the logic class specifically.  It's good practice to isolate which class and logic that you're testing, which is why the 
    //issue with Moq and these parameters came up.  
    public class TestedClass
    {
        //The ActionClass uses the interface in it's code so that we can use Moq for TDD and 
        //Dependency Injection for running the code in the live environment.
        private readonly ILogicClass _logicClass;
        public TestedClass(ILogicClass _logicClass)
        {
            this._logicClass = _logicClass;
        }

        //This method uses the logic class method "MethodWithOutParameter" and returns the value of the parameter passed into the method.
        public int MethodThatUsesMethodWithOutParameter()
        {
            int myOutValue;
            _logicClass.MethodWithOutParameter(out myOutValue);

            return myOutValue;
        }

        //This method uses the logic class method  "MethodWithObjectParameter" and returns an int with what the logic class did to the 
        //containerClass's MyProperty value. 
        public int MethodThatUsesMethodWithObjectParameter()
        {
            var containerClass = new ContainerClass();
            _logicClass.MethodWithObjectParameter(containerClass);

            return containerClass.MyProperty;
        }

        //This method uses the logic class method "MethodWithRefParameter" and returns the value of the int passed.  
        public int MethodThatUsesMethodWithRefParameter()
        {
            int myRefValue = 0;
            _logicClass.MethodWithRefParameter(ref myRefValue);

            return myRefValue;
        }

        //This method uses the logic class method  "MethodWithRefObjectParameter" and returns an int with what the logic class did to the 
        //containerClass's MyProperty value. 
        public int MethodThatUsesMethodWithRefObjectParameter()
        {
            var containerClass = new ContainerClass();
            _logicClass.MethodWithRefObjectParameter(ref containerClass);

            return containerClass.MyProperty;
        }
    }

    //This class is simply for contiaining a single int property so I can show how objects are passed both implicilty and explicitly by reference
    public class ContainerClass
    {
        public int MyProperty { get; set; }
    }

So basically, we have a class that we’re going to write tests for, and a logic class with an interface that does stuff in the backend. In our unit tests the logic class will be mocked up using Moq, and the TestedClass methods will be run in the tests.

Moq Setup with Object Parameter

This first unit test addresses my issue with the return value sql parameter above. The Moq “Callback” method is what is needed to be able to make a change to an instance of a class that is passed in as a parameter. Indeed, Callback is basically a bit of code that is called when the method defined in the “Setup” is called in the test.
[Test]
        public void TestedClass_MethodThatUsesMethodWithObjectParameter_Test()
        {
            //Arrange
            var _logicClass = new Mock();
            var testedClass = new TestedClass(_logicClass.Object);
            _logicClass.Setup(x => x.MethodWithObjectParameter(It.IsAny())).Callback(
                (containerClass) =>
                    {
                        containerClass.MyProperty = 42;
                    }
                );

            //Act
            int result = testedClass.MethodThatUsesMethodWithObjectParameter();

            //Assert
            Assert.AreEqual(42, result);
        }
The syntax of “Callback” is a little interesting to get so here’s an explanation:

“Callback” without a parameter:


“Callback” with a parameter:


“Callback” with multiple parameters:


Moq Setup with Out Parameter

In the above section I talked about how to use the “Callback” method to change the value of an object passed in as a parameter in a method. In this section, I’m going to address using an out parameter. In this case the “Callback” method is not used. The value of the out parameter is simply set prior to the test and it will retain it’s set value for use in the calling method, as below:
[Test]
        public void TestedClass_MethodThatUsesMethodWithOutParameter_Test()
        {
            //Arrange
            var _logicClass = new Mock();
            var testedClass = new TestedClass(_logicClass.Object);

            int myOutValue = 42;
            _logicClass.Setup(x => x.MethodWithOutParameter(out myOutValue));

            //Act
            int result = testedClass.MethodThatUsesMethodWithOutParameter();

            //Assert
            Assert.AreEqual(42, result);
        }

This test passes because the expected value of the out parameter is set before the method is called and that value is not removed when the “MethodThatUsesMethodWithOutParameter” method is called. Because of this, the “Callback” method is not needed to set anything. Although it is not shown, this technique works for both primitive out parameters and instantiated objects used as out parameters.

Moq Fails when trying to alter Ref parameters

Moq does not allow the altering of parameters passed in by reference when the “ref” keyword is used. This issue is confused and I was failed in my google searches because of several reasons. First, apparently Moq used to allow this type of manipulation. Secondly, it was supposed to be in a release of Moq from 2009, and either never made it in or the functionality was removed on subsequent releases. Third, there are other mocking platforms that DO allow altering of parameters passed in using the “ref” keyword. The final reason why this was a confusing question to answer is because the Moq Wiki (https://github.com/Moq/moq4/wiki/Quickstart) actually says that ref parameters are allowed. Which it does, as shown in the quote below:
The above code does work, but there is no way of changing the passed in value. Additionally, the originally set value is removed and so the example tests included here fail.
[Test]
        public void TestedClass_MethodThatUsesMethodWithRefParameter_Test()
        {
            //Arrange
            var _logicClass = new Mock();
            var testedClass = new TestedClass(_logicClass.Object);

            int myRefValue = 42;
            _logicClass.Setup(x => x.MethodWithRefParameter(ref myRefValue));

            //Act
            int result = testedClass.MethodThatUsesMethodWithRefParameter();

            //Assert
            Assert.AreEqual(42, result);
        }

Confusingly, the unit tests also fail when an object is passed by reference using the ref keyword, even though objects are always passed by reference in .Net. You can see this by how the test below fails:
[Test]
        public void TestedClass_MethodThatUsesMethodWithRefObjectParameter_Test()
        {
            //Arrange
            var _logicClass = new Mock();
            var testedClass = new TestedClass(_logicClass.Object);
            var containerClass = new ContainerClass();
            containerClass.MyProperty = 42;
            _logicClass.Setup(x => x.MethodWithRefObjectParameter(ref containerClass));

            //Act
            int result = testedClass.MethodThatUsesMethodWithRefObjectParameter();

            //Assert
            Assert.AreEqual(42, result);
        }

Also, there is no way to implement a use of the “Callback” method when using the “ref” keyword in a tested method. “Callback” relies on using the type <T> generic functionality and you can’t include that keyword in using <T> notation and if you try to include it in the Action method parameter of the Callback method, it returns an error: “System.ArgumentException : Invalid callback. Setup on method with parameters (Int32&) cannot invoke callback with parameters (Int32).” The test below will produce that error:
[Test]
        public void TestedClass_MethodThatUsesMethodWithRefParameter_Test_TryingCallback()
        {
            //Arrange
            var _logicClass = new Mock();
            var testedClass = new TestedClass(_logicClass.Object);

            int myRefValue = 42;
            _logicClass.Setup(x => x.MethodWithRefParameter(ref myRefValue)).Callback(
                (myref) =>
                    {
                        myref = 42;
                    }
                );

            //Act
            int result = testedClass.MethodThatUsesMethodWithRefParameter();

            //Assert
            Assert.AreEqual(42, result);
        }

Conclusion: Moq is a fantastic testing framework and needing to alter a parameter in a mocked method so that the tested method can be exercised is an edge case. However, there was confusion information on the internet about how to get Callback to work, and using an “out” parameter. The search results were also annoyingly inconsistent about using the “ref” keyword. If the information I provided does not match with your understanding, please reply in the comments and I’ll make the necessary changes.

Sunday, November 2, 2014

TDD with Routes and RouteConfig in MVC 5

When doing TDD on an MVC app it's important to not neglect the code that's contained in the RouteConfig class. This is especially true if complex URLs are used to enhance the user experience with using and sharing URLs. Because this took me a little while to figure out, and because the process changed with MVC 5 (confusing google searches on the subject), here are some example tests to use in your application.

These examples are using Asp.Net MVC 5, NUnit, and Moq, but can be tailored to whatever testing and mocking frameworks you use.  

For the following tests this is the RouteConfig.RegisterRoutes method used:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.MapRoute(
        name: "Account",
        url: "Account/",
        defaults: new { controller = "Account", action = "Index" }
        );

    routes.MapRoute(
        name: "AccountWithAccountNo", 
        url: "Account/{accountNo}",
        defaults: new {controller="Account", action="Detail" }
        );
    routes.MapRoute(
        name: "AccountToUser",
        url: "Account/{accountNo}/User",
        defaults: new {controller = "User", action="Index"}
        );            
    routes.MapRoute(
        name: "AccountToUserWithGuid",
        url: "Account/{accountNo}/User/{userGuid}",
        defaults: new { controller = "User", action = "Edit" }
        );
    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
    );
} 

The trick that really makes this work is the AppRelativeCurrentExecutionFilePath property on the  HttpRequest object.  This property normally returns the path of the request  in a relative way (i.e. "~/Account").  This is helpful since it means you don't need to completely flesh out the HttpContext and Request objects in order to make your tests work, and it allows you to write the tests quickly.

To do a basic test for a URL path it's necessary to first Mock the HttpContextBase and then Mock up the Request object so that it returns the Path that is going to be tested. The Assert statements perform the tests to insure that the correct controller and action are called and that the correct parameters are included. In this first example, no extra parameters should be included.

[Test()]
public void RouteConfig_Account_Index_Test()
{
    //Arrange        
    //Create the mocked HttpContextBase
    var httpContextMock = new Mock();
    //Mock the return for the Request's URL.  The string value is the Relative Path
    //of the URL that would have been used.
    httpContextMock.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath)
                   .Returns("~/Account");            
    var routes = new RouteCollection();
    RouteConfig.RegisterRoutes(routes);           

    //Act
    RouteData routeData = routes.GetRouteData(httpContextMock.Object); 

    //Assert
    Assert.IsNotNull(routeData);
    //Tests to make sure the right controller will be called
    Assert.AreEqual(routeData.Values["controller"], "Account");
    //Tests to make sure the right action will be called
    Assert.AreEqual(routeData.Values["action"], "Index");
    //Tests to make sure that there is no id value given to the action used
    Assert.IsNull(routeData.Values["id"]);  
}

​In this next example, a more complex url is used and the test makes sure that the correct controller and action are called, and that both parameters are successfully included. 

[Test()]
public void RouteConfig_User_WithAccountNoAndUserGuid_Test()
{ 

    //Arrange        
    var httpContextMock = new Mock();
    //In this test a more complex url is used which includes the accountNo as well as the user
    //guid.  The Asserts below test to make sure that the right controller and action are called as well
    //as that the values are passed in correctly.  
    httpContextMock.Setup(c => c.Request.AppRelativeCurrentExecutionFilePath)
                   .Returns("~/Account/54321/User/788CB225-AD01-4D33-B724-5AC7220F5864");      

    var routes = new RouteCollection();
    RouteConfig.RegisterRoutes(routes); 

    //Act
    RouteData routeData = routes.GetRouteData(httpContextMock.Object); 

    //Assert
    Assert.IsNotNull(routeData);
    Assert.AreEqual(routeData.Values["controller"], "User");
    Assert.AreEqual(routeData.Values["action"], "Edit");
    Assert.IsNotNull(routeData.Values["accountNo"]);
    Assert.AreEqual(routeData.Values["accountNo"], "54321");
    Assert.IsNotNull(routeData.Values["userGuid"]);
    Assert.AreEqual(routeData.Values["userGuid"], "788CB225-AD01-4D33-B724-5AC7220F5864");
}


When using TDD on an MVC application, it's important to not neglect testing the RouteConfig object. These examples should help you do that.