Wednesday, 4 April 2012

Passing mocked parameters between AppDomains

Currently I have been doing a lot of Acceptance Testing which has become more and more complex of late. One of the issues was separating out our systems that all run in the same process in the context of our Acceptance Tests. These are all NUnit test which use StoryQ and are run by the ReSharper and TeamCity NUnit test runners. To achieve the separation each of the systems is run in it’s own AppDomain but where previously certain parts of the systems were mocked out we are now unable to pass the mocks or any object across the app domain boundaries that is either not serializable or inherits from MarshalByRefObject.

Fortunately there is a way, the following shows how it is possible to pass mocks across the AppDomain boundary with no change to implementation but with certain restrictions. I use the NSubstitute mocking framework which uses Castles DynamicProxy framework under the covers but it should also apply to other mocking frameworks as well.

Demonstration

I have created a test fixture that gives an example of what can and cannot be achieved with mocks. The following shows:

  • Test setup which creates an AppDomain and creates a MarshalByRefObject that will consume our mock object
  • Test teardown which unloads the AppDomain so everything is nice and clean for the next run.
  • Introduces the classes and interfaces that will be used in the tests to demonstrate passing the mocks.
NB. All the assertions are done using the FluentAssertions framework.
[TestFixture]
class When_passing_mocks_to_an_app_domain
{
    private MarshalByRefType marshalByRefObject;
    private AppDomain testAppDomain;
 
    [SetUp]
    public void SetUp()
    {
        string exeAssembly = GetType().Assembly.FullName;
 
        var ads = new AppDomainSetup
        {
            ApplicationBase = Environment.CurrentDirectory,
            DisallowBindingRedirects = false,
            DisallowCodeDownload = true,
            ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile
        };
 
        testAppDomain = AppDomain.CreateDomain("AD #2", null, ads);
 
        marshalByRefObject = (MarshalByRefType)testAppDomain.CreateInstanceAndUnwrap(exeAssembly, typeof(MarshalByRefType).FullName);
 
    }
 
    [TearDown]
    public void TearDown()
    {
        AppDomain.Unload(testAppDomain);
    }
}
 
public interface IThing
{
    string ValueProperty { get; }
    string ValueMethod(int i);
    Foo ComplexValue();
    IBar ComplexInterface();
}
 
public class Foo
{
    public string Value()
    {
        return "Foo";
    }
}
 
public interface IBar
{
    string Value();
}
 
public class MarshalByRefType : MarshalByRefObject
{
    public string GettingASimpleString(IThing thingMock)
    {
        return thingMock.StringMethod(1);
    }
 
    public string GettingAStringFromAComplexType(IThing thingMock)
    {
        return thingMock.ComplexType().Value();
    }
 
    public string GettingAStringFromAComplexInterface(IThing thingMock)
    {
        return thingMock.ComplexInterface().Value();
    }
}

Proving our regular mocks don’t work

Here we have an example of how we would normally create a mock which is then passed it to our MarshalByRefObject.

[Test]
public void Should_pass_mock()
{
    var stringThing = Substitute.For<IThing>();
 
    stringThing.ValueProperty.Returns("wibble");
 
    //This is going to throw an exception
    var result = marshalByRefObject.GettingASimpleString(stringThing);
 
    result.Should().Be("wibble");
}

Running the above generates a System.Runtime.Serialization.SerializationException

System.Runtime.Serialization.SerializationException : Type 'NSubstitute.Core.CallRouter' in Assembly 'NSubstitute, Version=0.1.3.0, Culture=neutral, PublicKeyToken=92dd2e9066daa5ca' is not marked as serializable.

This is because or mock is not serializable nor does it inherit from MarshalByRefObject . With NSubstitute we have a nice little work around, we can tell NSubstitute to create a mock that implements our interface and inherit from MarshalByRefObject .

var stringThing = Substitute.For<IThing, MarshalByRefObject>();

Passing our mocked proxy

The following test is the same as our previous test except that it generates our mock using the above technique, this time it passes.

[Test]
public void Should_pass_a_mock_that_has_a_mocked_string_method()
{
    var stringThing = Substitute.For<IThing, MarshalByRefObject>();
 
    stringThing.ValueMethod(1).Returns("wobble");
 
    var result = marshalByRefObject.GettingASimpleString(stringThing);
 
    result.Should().Be("wobble");
}

Lets try something more adventurous

Dealing with simple types is relatively straight forward but what about mocking more complex objects. The following tries to use a method that returns a more complex concrete type.

[Test]
public void Should_pass_a_mock_that_has_a_mocked_complex_method()
{
   var stringThing = Substitute.For<IThing, MarshalByRefObject>();
   stringThing.ComplexType().Returns(new Foo());
 
   //This is going to fail because Foo is not Serializable
   var result = marshalByRefObject.GettingAStringFromAComplexType(stringThing);
 
   result.Should().Be("Foo");
}

This fails for the same reason as our first test did except this time it fails inside our new AppDomain, it is worth noting when this fails, not when passing the object in but when trying to use Foo. This is because our MarshalByRefObject instance in the test is a proxy and only passes things across to the other AppDomainwhen it needs it. Unfortunately there is not much we can do in this situation without changing the structure of our code as we will see.

Here we have an example which is similar to the above except this time we are going to use a method that returns an interface. By default NSubstitute will create a mock for any method on a mock that returns an interface, unfortunately we cannot use these as we will be in the same situation as the previous example so we will have to mock the methods return values by hand.

[Test]
public void Should_pass_a_mock_that_has_a_mocked_complex_method_with_interface_using_marshalbyrefobject()
{
    var stringThing = Substitute.For<IThing, MarshalByRefObject>();
    var complexInterface = Substitute.For<IBar, MarshalByRefObject>();
    complexInterface.Value().Returns("Bar");
    stringThing.ComplexInterface().Returns(complexInterface);
 
    var result = marshalByRefObject.GettingAStringFromAComplexInterface(stringThing);
 
    result.Should().Be("Bar");
}

Limitations and things to watch for

As you have seen it is possible to create mocks that can be serialized but there are some caveats:

  1. You need to inherit your mock from the MarshalByRefObject base class which rules out concrete types that are not already serializable.
  2. Extra leg work to mock more complex types.
  3. If you have not mocked something correctly you will only know at the point when you come to use it which could make debugging confusing.

4 comments:

  1. Thank you for the great article I did enjoyed reading it. If you are a students and looking for best Chicago title format assignment then you can visit my page for complete information and also get assignment at best price.
    Visit: Chicago Title Format Assignment Help

    ReplyDelete
  2. Great job on shedding light on the complexities and benefits of 3PL Logistics Solution in India. A must-read for any business eyeing efficient operations.

    ReplyDelete
  3. Incredibly informative blog on bonded warehouses in India! Clears up many misconceptions and offers valuable insights for businesses navigating import-export procedures

    ReplyDelete
  4. Informative blog on Warehouse Management System! Clear and concise overview, perfect for businesses looking to optimize their warehouse operations.

    ReplyDelete