C# 如何使用私有setter将模拟接口分配给属性进行单元测试?
基本上,我使用MEF导出类并获得“公共”导入。当我想测试这些类时,我可以创建IFoo的模拟接口,但是如何使用privatesetter将其实际应用到其中呢?MEF能够以某种方式处理它,我不确定如何测试我的DoSomething方法。我认为唯一的方法是使用反射。我认为唯一的方法是使用反射。C# 如何使用私有setter将模拟接口分配给属性进行单元测试?,c#,unit-testing,reflection,moq,mef,C#,Unit Testing,Reflection,Moq,Mef,基本上,我使用MEF导出类并获得“公共”导入。当我想测试这些类时,我可以创建IFoo的模拟接口,但是如何使用privatesetter将其实际应用到其中呢?MEF能够以某种方式处理它,我不确定如何测试我的DoSomething方法。我认为唯一的方法是使用反射。我认为唯一的方法是使用反射。MyAbstractClass依赖于IFoo,但您没有明确说明它。应添加构造函数以使依赖项显式: [InheritedExport(typeof(MyAbstractClass)) public abstract
MyAbstractClass
依赖于IFoo
,但您没有明确说明它。应添加构造函数以使依赖项显式:
[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.
}
}
现在您可以使用mock轻松地测试它
所以,我把你们的课改写成这样:
public MyAbstractClass(IFoo foo) { this.Foo = foo; }
[InheritedExport(typeof(MyAbstractClass))
公共抽象类MyAbstractClass{
私有只读IFoo;
公共伊福{
得到{
Contract.sure(Contract.Result()!=null);
返回此.foo;
}
}
受保护的MyAbstractClass(IFoo-foo){
Contract.Requires(foo!=null);
this.foo=foo;
}
}
公共类MyClass:MyAbstractClass
{
[导入构造函数]
公共MyClass(IFoo-foo):基(foo){}
}
否则,您必须使用反射来获取私有setter。这太恶心了。
MyAbstractClass
依赖于IFoo
,但您没有将其显式化。您应该添加一个构造函数以使依赖显式化:
[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.
}
}
现在您可以使用mock轻松地测试它
所以,我把你们的课改写成这样:
public MyAbstractClass(IFoo foo) { this.Foo = foo; }
[InheritedExport(typeof(MyAbstractClass))
公共抽象类MyAbstractClass{
私有只读IFoo;
公共伊福{
得到{
Contract.sure(Contract.Result()!=null);
返回此.foo;
}
}
受保护的MyAbstractClass(IFoo-foo){
Contract.Requires(foo!=null);
this.foo=foo;
}
}
公共类MyClass:MyAbstractClass
{
[导入构造函数]
公共MyClass(IFoo-foo):基(foo){}
}
否则,您必须使用反射来获取私有setter。这太恶心了。尝试在构造函数中传递它:
[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) { }
}
并将抽象类中的“私有集”更改为“受保护集”。尝试在构造函数中传递它:
[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) { }
}
并将抽象类中的“私有集”更改为“受保护集”。我相信使用MS Moles框架可以实现这一点:
我相信您可以通过MS Moles框架实现这一点:
反射是最好的方法。我喜欢在基本测试程序集中创建一个扩展方法,它具有访问/设置私有成员等有用功能 另一种选择(如果可以将setter设置为受保护的而不是私有的-这里可能是这种情况,也可能不是这种情况,但是如果您确实有一个具有类似愿望的受保护成员)让你的测试子类被测试的类。这感觉很脏,看起来不是一个好主意,但我想不出一个实际的原因为什么它不好,并且可以实现这里的目标
class MyClass : MyAbstractClass
{
public MyClass (IFoo foo)
{
Foo = foo;
}
}
公共静态类ReflectionExtensions
{
公共静态T GetPrivateFieldValue(此对象实例,字符串字段名)
{
var field=instance.GetType().GetField(fieldName,BindingFlags.instance | BindingFlags.NonPublic | BindingFlags.Public);
如果(字段!=null)
{
return(T)field.GetValue(实例);
}
抛出新ArgumentException(“无法找到指定的字段。”,“fieldName”);
}
公共静态void SetReadonlyProperty(此对象实例,字符串propertyName,对象值)
{
instance.GetType().GetProperty(propertyName).SetValue(instance,value,null);
}
公共静态void SetStaticCreadonlyProperty(此类型、字符串属性名称、对象值)
{
type.GetProperty(propertyName).GetSetMethod(true).Invoke(null,new[]{value});
}
}
反射是最好的方法。我喜欢在基本测试程序集中创建一个扩展方法,它具有访问/设置私有成员等有用功能
另一种选择(如果可以将setter设置为受保护的而不是私有的-这里可能是这种情况,也可能不是这种情况,但是如果您确实有一个具有类似愿望的受保护成员)让你的测试子类被测试的类。这感觉很脏,看起来不是一个好主意,但我想不出一个实际的原因为什么它不好,并且可以实现这里的目标
class MyClass : MyAbstractClass
{
public MyClass (IFoo foo)
{
Foo = foo;
}
}
公共静态类ReflectionExtensions
{
公共静态T GetPrivateFieldValue(此对象实例,字符串字段名)
{
var field=instance.GetType().GetField(fieldName,BindingFlags.instance | BindingFlags.NonPublic | BindingFlags.Public);
如果(字段!=null)
{
return(T)field.GetValue(实例);
}
抛出新ArgumentException(“无法找到指定的字段。”,“fieldName”);
}
公共静态void SetReadonlyProperty(此对象实例,字符串propertyName,对象值)
{
instance.GetType().GetProperty(propertyName).SetValue(instance,value,null);
}
公共静态void SetStaticCreadonlyProperty(此类型、字符串属性名称、对象值)
{
type.GetProperty(propertyName).GetSetMethod(true).Invoke(null,new[]{value});
}
}
如果要保留MEF导入,最简单的方法是在每个属性上使用导入构造函数属性
而不是导入属性
s
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 });
}
}
}
这个解决方案有点糟糕,因为现在你必须让所有从MyAbstractClass扩展的类都使用一个ImportingConstructorAttribute
并调用base()
。如果你的抽象类被到处使用,尤其是当它决定添加另一个导入的属性时,这可能会变得非常糟糕