我有一个c#类,它实例化自己的NetworkCommunicator
类。我想为我的单元测试模拟出NetworkCommunicator
类,并用一个非常简单的存根替换它。
但是NetworkCommunicator
从来没有作为参数传递。它由被测试的类创建。
Moq
或类似的东西? 您提到对于这个项目来说,DI太重量级了,为什么不试试卡车司机的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();
}
}
使用这种构造函数链接技术,您现在可以随心所欲地模拟独立性,而不必担心连接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;
}
}
我在"单元测试的艺术"中读到过这种技术,当重构到完全依赖注入没有意义时,或者当我测试的类不能改变时,我经常使用它。
您应该重构代码并传入依赖项。在Visual Studio 2012中,您还可以使用typemock作为更容易使用的替代品。
这是内置的Fakes系统,在http://msdn.microsoft.com/en-us/library/hh549175.aspx
有很好的描述如果这对你的用例来说太重了,你可能会发现PrivateObject类更有用。
正如你所注意到的,当你想模拟这个东西时,这是c#中的show stopper。解决方案很简单,取决于实例化类的上下文/目的:我有一个c#类,它自己实例化了一个NetworkCommunicator类。
- 如果它是可重用的组件,则将其作为依赖注入
- 如果每次有需求时都应该创建,则通过工厂提供
无论哪种方式,都需要DI(第二个示例中的工厂也被自然注入)。
在Java中,这就是你需要依赖注入的原因,它对这个项目来说太重了。
依赖注入太重了吗?DI是一种设计模式,只有当不是真正需要时才会使用它。你的问题清楚地表明你需要它。也许你的意思是,DI容器对你的项目太重了?这可能是真的,因为根据项目的复杂性,您应该选择合适的方式来应用DI。
我想再提出一点,在应用Greg Smith的回答中提出的解决方案时要注意。本质上,你的API以构造函数结束:
public TestedClass() : this(new Dependency()) ...
public TestedClass(IDependency) ...
乍一看可能很吸引人,但当考虑到长期的观点时,几个问题就开始出现了:
-
TestedClass
必须有IDependency
还是没有它可以做得很好? - 什么默认(无参数构造函数)默认为(实现细节级别的知识需要正确使用它)?
- 它创建了紧密耦合的组件(
TestedClass
组件可能必须引用其他组件-Dependency
的组件,即使它可能与它无关)
这是一个不同名称的反模式,例如私生子注入。当然,其中一些问题可能会得到缓解(比如将构造函数设置为protected/internal或在同一程序集中使用默认实现),但是反模式及其长期后果仍然存在。还要注意,它并不比常规DI更简单、更快或更少代码。
你必须问自己什么更轻 -应用适当的DI,还是使用反模式和/或第三方框架(MS Fakes)。