michael
michael

Reputation: 15282

How to assign a mocked interface to a property with a private setter for unit testing?

[InheritedExport(typeof(MyAbstractClass))
public abstract class MyAbstractClass
{
    [Import] protected IFoo Foo { get; private set; }
}

public sealed class MyClass : MyAbstractClass
{
    public void DoSomething()
    {
        if (Foo == null) throw new Exception();

        var = Foo.GetBar();
        //etc.
    }
}

Basically, I use MEF to export classes out and get "common" imports. When I want to test these classes I can create mock interfaces of IFoo, but how do I actually get it in there with the private setter? MEF somehow is able to handle it, and I'm not sure how else I can test my DoSomething method.

Upvotes: 1

Views: 1383

Answers (6)

Dave Rael
Dave Rael

Reputation: 1759

reflection is the best way to do it. i like to create an extension method in a base test assembly with useful functions like accessing/setting private members and such.

another option (if it is acceptable to make the setter protected instead of private - which may or may not be the case here, but if you do have a protected member with a similar desire) would be have your test subclass the class under test. it feels dirty and doesn't seem like a good idea, but i can't think of a practical reason why it's bad and would accomplish the objective here.

public static class ReflectionExtensions
{
    public static T GetPrivateFieldValue<T>(this object instance, string fieldName)
    {
        var field = instance.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
        if (field != null)
        {
            return (T) field.GetValue(instance);
        }
        throw new ArgumentException("The field specified could not be located.", "fieldName");
    }

    public static void SetReadonlyProperty(this object instance, string propertyName, object value)
    {
        instance.GetType().GetProperty(propertyName).SetValue(instance, value, null);
    }

    public static void SetStaticReadonlyProperty(this Type type, string propertyName, object value)
    {
        type.GetProperty(propertyName).GetSetMethod(true).Invoke(null, new[] { value });
    }
}

Upvotes: 1

jason
jason

Reputation: 241641

MyAbstractClass has a dependency on IFoo but you're not making it explicit. You should add a constructor to make the dependency explicit:

public MyAbstractClass(IFoo foo) { this.Foo = foo; }

Now you can easily test it using your mock.

So, I'd rewrite your class like this:

[InheritedExport(typeof(MyAbstractClass))
public abstract class MyAbstractClass {
    private readonly IFoo foo;
    public IFoo Foo {
        get {
            Contract.Ensures(Contract.Result<IFoo>() != null);  
            return this.foo;
        }
    }

    protected MyAbstractClass(IFoo foo) {
        Contract.Requires(foo != null);
        this.foo = foo;
    }
}

public class MyClass : MyAbstractClass
{
    [ImportingConstructor]
    public MyClass(IFoo foo) : base(foo) { }
}

Otherwise, you have to use reflection to get at the private setter. That's disgusting.

Upvotes: 1

myermian
myermian

Reputation: 32515

If you want to preserve your MEF Imports, the easiest way to do so is to use the ImportingConstructorAttribute instead of ImportAttributes on each property.

[InheritedExport(typeof(MyAbstractClass))
public abstract class MyAbstractClass
{
    [ImportingConstructor]
    protected MyAbstractClass(IFoo foo)
    {
        //BONUS! Null check here instead...
        if (foo == null) throw new NullArgumentException("foo");

        Foo = foo;
    }

    protected IFoo Foo { get; private set; }
}

public sealed MyClass : MyAbstractClass
{
    [ImportingConstructor]
    public MyClass(IFoo foo) : base(foo) { }

public void DoSomething()
{
    var = Foo.GetBar();
    //etc.
}

}

The solution kind of stinks because now you have to have all classes that extend from MyAbstractClass each use an ImportingConstructorAttribute and call base(). This can get quite ugly if your abstract class is used all over and especially if it decides to add another imported property... now you have to change the constructor signature.

I'd stick with ugly reflection... better ugly unit tests than ugly code.

Upvotes: 3

Peter T. LaComb Jr.
Peter T. LaComb Jr.

Reputation: 2975

I believe you can accomplish this with the MS Moles framework:

http://research.microsoft.com/en-us/projects/moles/

Upvotes: 1

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112342

Try to pass it in the constructor:

class MyClass : MyAbstractClass
{
    public MyClass (IFoo foo)
    {
        Foo = foo;
    }
}

and change the "private set" in your abstract class to "protected set".

Upvotes: -1

Fischermaen
Fischermaen

Reputation: 12458

I think the only way is using reflection.

Upvotes: 1

Related Questions