我有一个 C# 类,它自己实例化一个NetworkCommunicator
类。我想NetworkCommunicator
为我的单元测试模拟这个类,并用一个非常简单的存根替换它。
但是NetworkCommunicator
永远不会作为参数传递。它是由正在测试的类创建的。
在 Ruby 中,这很容易模拟出来。在 Java 中,这就是你需要依赖注入的原因,这对于这个项目来说太重了。有没有一种简单的方法可以在 C# 中模拟它,也许使用Moq
或类似的东西?
我有一个 C# 类,它自己实例化一个NetworkCommunicator
类。我想NetworkCommunicator
为我的单元测试模拟这个类,并用一个非常简单的存根替换它。
但是NetworkCommunicator
永远不会作为参数传递。它是由正在测试的类创建的。
在 Ruby 中,这很容易模拟出来。在 Java 中,这就是你需要依赖注入的原因,这对于这个项目来说太重了。有没有一种简单的方法可以在 C# 中模拟它,也许使用Moq
或类似的东西?
您提到 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。
创建一个继承自被测类的“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 没有意义或我正在测试的类不是我可以改变的东西时经常使用它。
希望这可以帮助。
有内置的 Fakes 系统,在http://msdn.microsoft.com/en-us/library/hh549175.aspx中有很好的描述
如果这对您的用例来说太重了,您可能会发现PrivateObject类更有用。
我有一个 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)来解决问题。