单元测试.net中高度耦合的代码;反射注入嘲笑?

时间:2012-04-05 14:25:17

标签: c# .net unit-testing

假设我有一堆看起来像......的课程。

class Foo{
  private Bar highlyCoupled = new highlyCoupled();

  public bool DoTheThing(){
    return highlyCoupled.doesTheThing();
  } 
}

是否有可能使用反射打开foo和注入(duck-punch可能是一个更正确的术语)某种mockHighlyCoupled代替highCoupled?

在这种情况下怎么样......

class DoubleFoo : Foo{
  public bool DoTheOtherThing(){
    return DoTheThing();
  }
}

继承的highCoupled可以在其中插入模拟吗?

不幸的是,重构代码以便不需要反射。

4 个答案:

答案 0 :(得分:1)

您确实可以使用反射和模拟类型,但该类型必须从原始类型继承(否则FieldInfo.SetValue将失败)。

void Main()
{
    var bar = new Bar();
    var type = typeof(Bar);
    // Get the type and fields of FieldInfoClass.
    var fields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
    fields[0].SetValue(bar, new FooMock()); // you can use new Foo() here too.
    bar.Print();
}

class Foo {
    public int i = 0;
}

class FooMock : Foo {
}

class Bar {
    private Foo foo = new Foo();

    public void Print() {
        Console.WriteLine(i);
    }
}

答案 1 :(得分:1)

由于您无法使用模拟框架进行重构,因此可以使您更轻松。例如:

TypeMock:

var fakeType = Isolate.Fake.Instance<SomeType>();
ObjectState.SetField(fakeType, "_somePrivateField", myValue);

起订量:

var fakeType = new Mock<SomeType>()
fakeType.Protected().Setup<SomeType>("_somePrivateField").Returns(myValue);

老实说,我并没有真正尝试过Moq,但我认为它会做你需要的。

答案 2 :(得分:0)

如果你根本无法重构(使highlyCoupled受保护),那么你就会陷入使用反射的境地。这样您就可以在不修改的情况下设置highlyCoupled的值。

答案 3 :(得分:0)

我普遍同意罗布;如果你不能重构这个以使依赖关系更松散地耦合(至少允许派生类像测试代理一样覆盖它的默认值),那么尽管它的可见性设置值的反射几乎是唯一的你可以去的方式。

您可以做的绝对最少的是保护依赖项。如果现在或将来的任何时候都可以做到这一点,那就去做吧:

class Foo{
  protected Bar highlyCoupled = new highlyCoupled();

  public bool DoTheThing(){
    return highlyCoupled.doesTheThing();
  } 
}

...

//in your test suite    
class FooTestProxy:Foo
{
   public FooTestProxy(Bar testMock)
   {
      highlyCoupled = testMock;
   }   
}

//now when testing, instantiate your test proxy and pass it the mocked object