单独测试派生类的行为

时间:2014-04-16 10:19:31

标签: c# unit-testing mocking nsubstitute

我正在尝试隔离并测试现有代码库中的特定类,该代码库是从某个base派生的,并且有一个我想要模拟的私有类成员:

public class Derived : Base 
{ 
   protected Something<Other> _otherSomething = new Something<Other>();
}

具体来说,我想使用这些测试来验证Derived是否引发了某些事件DoSomething以回应_otherSomething举起ItHappened事件。

为了实现这一目标,我在我的测试项目中创建了一个MockedDerivedDerived

public class MockDerived : Derived
{
   public Something<Other> OtherSomething 
   { 
      set { _otherSomething = value; } 
   }
}

我正在使用 NSubstitute 作为我的MSTest测试方法的一部分来模拟一个类,我想做这样的事情:

[TestMethod]
public void TestMethod1()
{
   var sub = Substitute.For<Something<Other>>();
   MockedDerived md = new MockedDerived();
   bool wasRaised = false;
   md.DoSomething += (sender, args) => wasRaised = true;
   md.OtherSomething = sub;
   sub.itHappened += Raise.Event<ItHappenedEventHandler<Other>>(new ItHappenedEventArgs<Other>());
   Assert.IsTrue(wasRaised);
}

我遇到一些困难的地方是Base课程带来了相当多的行李,即它创造了一些其他课程的对象,并且有一些计时器,最终涉及到数据库。

它变得混乱。

我真正想做的就是确保这个Derived课程表现出特定的行为并按预期做出回应。

有关如何将此Derived行为的测试与凌乱的Base及其各种成员隔离开来的任何建议?也许我只是没有看到明显的解决方案。

2 个答案:

答案 0 :(得分:1)

您正面临着测试遗留代码的情况,其中您的基类采用具体的依赖关系,而不是依赖于接口。您可能想要参考Michael Feather的书“有效地使用遗留代码”中描述的Sprout方法和Sprout类。

答案 1 :(得分:1)

如果不知道您的代码正在做什么的细节或者您需要多少灵活性来改变那些,我可以考虑两种粗略的方法来尝试:将BaseDerived从它们中分离出来常见的依赖关系,或破坏继承链。

作为前者的一个例子,我们可以注入行李&#34;导致问题进入Base(我们也必须将问题提供给Derived)。然后我们可以在测试中伪造它,以使我们的测试不受定时器和数据库的影响。

public class Base {
  public Base(DatabaseStuff d, TimerStuff t) { ... }
}
public class Derived : Base {
  public Derived(DatabaseStuff d, TimerStuff t, Something<Other> something) : base(d,t) {...}
}

对于第二个选项,我们可以尝试用组合替换继承。我们可以让Base引用Derived而不是继承Base,而是委托给public class Derived { //TODO: Rename to `NotSoDerived` public Derived(Base b, Something<Other> something) { ... } public ItHappenedEventHandler<Other> itHappened ...; public void SomeBaseMethod() { b.SomeBaseMethod(); // let b handle this call } }

Base

现在我们可以注入假null(或Base &lt; shudder&gt; ,如果您的测试未能调用Something<Other>)和嘲笑Derived,我们可以单独测试Derived的这一方面。

如果您需要互换使用BaseIBase,那么您可以查看提取Base接口(如果有意义),然后Derived和{{1}可以实现它。