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.

3 comments:

  1. I like that I can copy and paste from your code examples. Thanks!

    ReplyDelete
  2. I like that you pointed out that creating a callback for a ref parameter is not possible. Are you sure that's true? I read on some thread that it should be possile but saw no solution. Would be annoying if it wouldn't be possible...

    ReplyDelete
    Replies
    1. If I remember correctly, there used to be a way to do this in an earlier version of Moq, but it doesn't work in the current versions. This removal of the feature is one of the main reasons for the confusion on the internet about if this is currently possible. I tried every different combination that I could think of to get it to work, without success.

      If you can find a solution I'd love to hear it and I'd correct this post if you do. Thanks.

      Delete