在.net中对高度耦合的代码进行单元测试;反射以注入mock

本文关键字:单元测试 反射 mock 注入 代码 net 高度 耦合 | 更新日期: 2023-09-27 18:30:03

假设我有一堆类,看起来像。。。

class Foo{
  private Bar highlyCoupled = new highlyCoupled();
  public bool DoTheThing(){
    return highlyCoupled.doesTheThing();
  } 
}

是否可以使用反射打开foo并在highlyCoupled的位置注入某种mockHighlyCouple(duck-panch可能是一个更正确的术语)?

在这种情况下。。。

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

继承的highlyCoupled可以在它的位置插入一个mock吗?

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

在.net中对高度耦合的代码进行单元测试;反射以注入mock

因为您不能使用mocking框架进行重构,所以这对您来说会更容易一些。例如:

TypeMock:

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

Moq:

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

老实说,我还没有和莫一起尝试过,但我认为它会满足你的需要。

您确实可以使用反射和模拟类型,但该类型必须从原始类型继承(否则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);
    }
}

如果你根本不能重构(使highlyCoupled受到保护),那么你就只能使用反射了。这将允许您在不进行修改的情况下设置highlyCoupled的值。

我大体上同意Rob的观点;如果你不能重构它以使依赖关系更松散地耦合(至少允许像测试代理这样的派生类覆盖其默认值),那么反射来设置值(尽管它是可见的)几乎是你唯一可以走的路。

你能做的最起码的事情就是保护依赖关系。如果这在现在或未来的任何时候都是可能的,那么就去做:

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