Jon Falkenstein
Jon Falkenstein

Reputation: 135

How to Unit Test with fakes when the class you're testing uses reflection in C#

I'm trying to unit test a factory class that uses reflection to construct an unknown object that derives from a known base class.

However, I'm getting an exception when the factory tries to invoke the the constructor derived from my fake unit's type. It's a null reference exception.

I'm using FakeItEasy. Here's What I have going on here:

[TestFixture]
public class DisplayUnitFactoryTests
{
    private readonly IDisplayUnitPluginContainer _mgr = A.Fake<IDisplayUnitPluginContainer>();
    private readonly DisplayUnitPlugin _plgin = A.Fake<DisplayUnitPlugin> ();
    private DisplayUnit _unit;
    private Guid _guid;

    [TestFixtureSetUp]
    public void init()
    {
        _unit = A.Fake<DisplayUnit> (p=> p.WithArgumentsForConstructor(new object[]{new Dictionary<string,string>()}));
        _guid = Guid.NewGuid ();

        A.CallTo (() => _mgr.Resolve (VALID_STRING)).Returns (_plgin);
        A.CallTo (() => _mgr.Resolve (INVALID_STRING)).Returns (null);
        A.CallTo (() => _plgin.DisplayUnitType).Returns (_unit.GetType ());
    }

That bottom line is my issue. DisplayUnit is an abstract class. It's implementation shouldn't matter in this situation except for its constructor. However, DisplayUnit has a constructor that requires a Dictionary as a parameter.

If I use a REAL type (not faked), the code works fine. (e.g. A.CallTo(() => _plgin.DisplayUnitType).Returns(typeof(TextUnit)); However, That real type requires a dependency outside my core code. I want different types of DisplayUnits to be added at runtime as plugins. Thus, I want to unit test my DisplayUnitFactory using a fake Display Unit. In this test, I shouldn't need to depend on an outside assembly in order to make this work.

Here's my test.

[Test]
public void InstantiateNew_ValidPluginID_EmptyDict_ReturnsCorrectDisplayUnit()
{
    var factory = new DisplayUnitFactory (_mgr);

    var du = factory.InstantiateNew (VALID_STRING, new Dictionary<string, string> ());
    Assert.That (du, Is.Not.Null);
}

Here's my factory code:

public DisplayUnit InstantiateNew (string pluginId, Dictionary<string, string> attributes)
{
    return getDisplayUnit (pluginId, attributes);
}

private DisplayUnit getDisplayUnit(string pluginId, Dictionary<string,string> attributes)
{
    //Get the constructor that accepts only a dictionary<string,string>.
    var ctor = getCtor (pluginId, new Type[]{typeof(Dictionary<string,string>)});
    //Invoke it with the attributes dictionary.
    var unit = ctor.Invoke (new object[]{ attributes }) as DisplayUnit;
    return unit;
}

private ConstructorInfo getCtor(string pluginId, Type[] paramTypes)
{
    var plugin = _container.Resolve (pluginId);
    if (plugin == null)
        throw new NotRegisteredPluginException ("Plugin not registered: " + pluginId);
    var type = plugin.DisplayUnitType;
    return type.GetConstructor (paramTypes);
}

Essentially, I need to fake the ConstructorInfo that will come out using Reflection, and ultimately fake the return from ConstructorInfo.Invoke(). But I'm getting a null reference exception in getDisplayUnit() when the constructor is invoked, because the ctor is coming back null.

Help!

Upvotes: 1

Views: 750

Answers (1)

Jon Falkenstein
Jon Falkenstein

Reputation: 135

So Thanks to Blair, I believe I've found the solution: It's not to use a Fake at all.

The faked type will have only a single public constructor, with arguments Castle.DynamicProxy.IInterceptor[], Dictionary<System.String,System.String>. Thus the whole process was breaking down when Reflection tried to get a constructor.

The solution was to create a DummyDisplayUnit that derived from DisplayUnit and use that instead. This worked just fine, without needing a fake.

I guess sometimes I try to abstract to the point where I miss the obvious solution...

Upvotes: 3

Related Questions