2

我有一个 C# 类,它自己实例化一个NetworkCommunicator类。我想NetworkCommunicator为我的单元测试模拟这个类,并用一个非常简单的存根替换它。

但是NetworkCommunicator永远不会作为参数传递。它是由正在测试的类创建的。

在 Ruby 中,这很容易模拟出来。在 Java 中,这就是你需要依赖注入的原因,这对于这个项目来说太重了。有没有一种简单的方法可以在 C# 中模拟它,也许使用Moq或类似的东西?

4

5 回答 5

5

您提到 DI 对于这个项目来说太重了,为什么不尝试一些 Truck Driver 的 DI,因此:

public interface IDependency
{
    void DoSomeStuff();
}

public class ClassUnderTest
{
    private IDependency _dependency;

    public ClassUnderTest(IDependency dependency)
    {
        _dependency = dependency;
    }

    public ClassUnderTest() : this(new Dependency())
    {}

    public void ImportantStuff()
    {
        _dependency.DoSomeStuff();
    }
}

使用这种构造函数链接技术,您现在可以随心所欲地模拟 IDependency,而不必担心连接 DI 或 IoC。

于 2012-12-31T00:20:46.487 回答
3

您应该重构您的代码并传递依赖项。您还可以使用typemock作为Visual Studio 2012 中更容易使用的替代品

于 2012-12-31T00:16:10.673 回答
3

创建一个继承自被测类的“TestClass”。用模拟实例覆盖该参数 在被测类上创建一个返回新实例的属性

public class ClassUnderTest {

public string MethodYouAreTesting(int someInput) {

var networkCommunicator = GetNetworkCommunicator();
// Do some stuff that I might want to test
return "foo";

}

public virtual NetworkCommunicator GetNetworkCommunicator {
    return new NetworkCommunicator();
}

}

[TestFixture]
public class ClassUnderTestTests {
public void GivenSomeCondition_MethodYouAreTesting_ReturnsFooString() {

var classToTest = new TestClassUnderTest();
var result = classToTest.MethodYouAreTesting(1);
Assert.That(result, Is.EqualTo("foo");

}
}

public class TestClassUnderTest : ClassUnderTest {

public override GetNetworkCommunicator {
return MockedNetworkCommunicator;
}

}

我在“单元测试的艺术”中读到了这种技术,并在重构为完全 DI 没有意义或我正在测试的类不是我可以改变的东西时经常使用它。

希望这可以帮助。

于 2012-12-31T00:26:12.470 回答
1

有内置的 Fakes 系统,在http://msdn.microsoft.com/en-us/library/hh549175.aspx中有很好的描述

如果这对您的用例来说太重了,您可能会发现PrivateObject类更有用。

于 2012-12-31T00:20:25.883 回答
1

我有一个 C# 类,它自己实例化一个 NetworkCommunicator 类。

正如您所注意到的,当您想模拟这个东西时,这是 C# 中的一个展示停止器。解决方案很简单,取决于实例化类的上下文/目的:

  • 如果它是可重用组件,则将其作为依赖项注入
  • 如果每次有需求时都应该创建它,则通过工厂提供它

无论哪种方式,您都需要 DI(第二个示例中的工厂也是自然注入的)。

在 Java 中,这就是你需要依赖注入的原因,这对于这个项目来说太重了。

依赖注入太重了吗?DI是设计模式,只是在不需要的时候使用它太重了。你的问题清楚地表明你需要它。也许您的意思是DI 容器对您的项目来说太重了?这可能是真的,因为根据项目的复杂性,您应该选择适当的方式来应用 DI

在应用像Greg Smith's answer中提出的解决方案时,我想再提一点需要注意。本质上,您的 API 以构造函数结束:

public TestedClass() : this(new Dependency()) ...
public TestedClass(IDependency) ...

乍一看很吸引人,但如果考虑到长远的眼光,就会出现几个问题:

  • TestedClass 必须有 还是没有IDependency它可以做得很好?
  • 什么默认值(无参数构造函数)默认为(正确使用它需要实现细节级别的知识)?
  • 它创建了紧密耦合的组件(TestedClass组件可能必须引用其他组件 -Dependency的组件,即使它可能与它无关)

这是一个使用不同名称的反模式,例如Bastard Injection。当然,其中一些问题可能会得到缓解(例如使构造函数受保护/内部或在同一程序集中具有默认实现),但反模式及其长期后果仍然存在。另请注意,它绝不比常规 DI 更简单、更快或更少代码。

您将不得不问自己什么是不那么繁重的- 应用适当的 DI,或者使用反模式和/或 3rd 方框架(MS Fakes)来解决问题。

于 2012-12-31T12:06:59.467 回答