[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.
}
}
基本上,我使用MEF导出类并获得“常见”导入。当我想测试这些类时,我可以创建IFoo的模拟接口,但是如何使用私有setter实际获得它? MEF以某种方式能够处理它,我不知道我还能怎样测试我的DoSomething方法。
答案 0 :(得分:3)
如果您想保留MEF Imports,最简单的方法是在每个媒体资源上使用ImportingConstructorAttribute
代替ImportAttribute
。
[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.
}
}
解决方案有点臭,因为现在你必须让所有从MyAbstractClass扩展的类都使用ImportingConstructorAttribute
并调用base()
。如果你的抽象类被全部使用,尤其是如果它决定添加另一个导入的属性,那么这会非常难看......现在你必须改变构造函数签名。
我坚持用丑陋的反思......比丑陋的代码更好的丑陋单元测试。
答案 1 :(得分:1)
我认为唯一的方法是使用反射。
答案 2 :(得分:1)
MyAbstractClass
依赖于IFoo
,但您并未将其明确化。您应该添加构造函数以使依赖项显式:
public MyAbstractClass(IFoo foo) { this.Foo = foo; }
现在您可以使用模拟轻松测试它。
所以,我会像这样改写你的课程:
[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) { }
}
否则,您必须使用反射来获取私有的setter。那太恶心了。
答案 3 :(得分:1)
我相信你可以通过MS Moles框架实现这一目标:
答案 4 :(得分:1)
反射是最好的方法。我想在基础测试程序集中创建一个扩展方法,其中包含访问/设置私有成员等有用的功能。
另一个选项(如果可以使setter受保护而不是私有 - 这可能是或不是这里的情况,但如果你确实有一个有类似愿望的受保护成员)将让你的测试子类成为类在测试中。它感觉很脏,看起来不是一个好主意,但我想不出一个实际的原因,为什么它很糟糕,并将在这里实现目标。
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 });
}
}
答案 5 :(得分:-1)
尝试在构造函数中传递它:
class MyClass : MyAbstractClass
{
public MyClass (IFoo foo)
{
Foo = foo;
}
}
并将抽象类中的“私有集”更改为“受保护集”。