C# 从容器测试接口是好的还是坏的做法?

C# 从容器测试接口是好的还是坏的做法?,c#,unit-testing,C#,Unit Testing,请帮助我澄清以下设计方法: 在我们的单元测试项目中,我们使用的是UnityContainer。在每次测试开始时,我们都会通过该容器解析要测试的对象,如下所示: IObjectToTest objectToTest = container.Resolve<IObjectToTest>(); IObjectToTest objectToTest=container.Resolve(); 在Unity配置中,我们配置了IObjectToTest的具体实现。 在测试中,我们测试这个接口I

请帮助我澄清以下设计方法: 在我们的单元测试项目中,我们使用的是
UnityContainer
。在每次测试开始时,我们都会通过该容器解析要测试的对象,如下所示:

IObjectToTest objectToTest = container.Resolve<IObjectToTest>();
IObjectToTest objectToTest=container.Resolve();
在Unity配置中,我们配置了
IObjectToTest
的具体实现。 在测试中,我们测试这个接口
IObjectToTest
objectToTest,不管对象的具体实例如何


问题是:这种方法的利弊是什么?为什么我们不能实例化新的
ObjectToTest
(具体实现)并测试它呢?为什么我们要使用统一和接口?

这个方法一点也不奇怪,实际上它比你想象的更常见。当您想要编写可测试代码时,您会发现自己使用了大量的依赖注入


好处是显而易见的,您可以轻松地交换具体的实现,而无需重新编写所有测试,您可以删除实现并模拟它们以只测试您感兴趣的内容。因此,简单地说,您不仅在测试接口,而且在通过容器测试(并注入)具体实现。这是一种非常常见的方法,始终通过接口使用依赖项注入是一种很好的实践。

好吧,这种方法一点也不奇怪,实际上它比您想象的更常见。当您想要编写可测试代码时,您会发现自己使用了大量的依赖注入


好处是显而易见的,您可以轻松地交换具体的实现,而无需重新编写所有测试,您可以删除实现并模拟它们以只测试您感兴趣的内容。因此,简单地说,您不仅在测试接口,而且在通过容器测试(并注入)具体实现。这是一种非常常见的方法,始终通过接口使用依赖项注入是一种很好的做法。

对接口进行测试通常被认为是一种最佳做法。您针对接口而不是具体实现进行测试的原因是创建实际的“单元”测试,而不是集成测试

如果您使用被测对象对依赖类的具体实现进行测试,那么您并不是在真正测试单元,而是在测试集成。最终,集成在生产代码中是很重要的,但是如果对单个单元进行了彻底和正确的测试,那么就没有什么好担心的了

通常,您仍然会编写一个集成测试来验证完整的连接实现,但是会更多地关注单元测试

在下面的示例中,我们测试
iobjectandertest
的行为,而不考虑
IBusinessLogicObject
的实现。我们通过使用框架模拟依赖关系来验证两个对象之间的协作。通过这样做,我们只需验证两个对象之间是否发生了必要的协作,而不必关心IBusinessLogicObject的内部。这些问题更适合我们为IBusinessLogicObject编写的单元测试

测试示例

IUnityContainer container = new UnityContainer();
Mock<IBusinessLogicObject> businessLogicObject = new Mock<IBusinessLogicObject>();
container.RegisterInstance<IBusinessLogicObject>(businessLogicObject.Object);
businessLogicObject
    .Setup(bl => bl.SomeMethod("some-stub-parameter"))
    .Returns("some expected value")

IObjectUnderTest subject = container.Resolve<IObjectUnderTest>();
var emptyResult = subject.MethodToBeTested("some-stub-parameter", "another-value");
Assert.AreEqual(string.Empty, emptyResult);
var result = subject.MethodToBeTested("some-stub-parameter", "businessLogic");
Assert.AreEqual("some expected value", result);

根据接口进行测试通常被认为是最佳实践。您针对接口而不是具体实现进行测试的原因是创建实际的“单元”测试,而不是集成测试

如果您使用被测对象对依赖类的具体实现进行测试,那么您并不是在真正测试单元,而是在测试集成。最终,集成在生产代码中是很重要的,但是如果对单个单元进行了彻底和正确的测试,那么就没有什么好担心的了

通常,您仍然会编写一个集成测试来验证完整的连接实现,但是会更多地关注单元测试

在下面的示例中,我们测试
iobjectandertest
的行为,而不考虑
IBusinessLogicObject
的实现。我们通过使用框架模拟依赖关系来验证两个对象之间的协作。通过这样做,我们只需验证两个对象之间是否发生了必要的协作,而不必关心IBusinessLogicObject的内部。这些问题更适合我们为IBusinessLogicObject编写的单元测试

测试示例

IUnityContainer container = new UnityContainer();
Mock<IBusinessLogicObject> businessLogicObject = new Mock<IBusinessLogicObject>();
container.RegisterInstance<IBusinessLogicObject>(businessLogicObject.Object);
businessLogicObject
    .Setup(bl => bl.SomeMethod("some-stub-parameter"))
    .Returns("some expected value")

IObjectUnderTest subject = container.Resolve<IObjectUnderTest>();
var emptyResult = subject.MethodToBeTested("some-stub-parameter", "another-value");
Assert.AreEqual(string.Empty, emptyResult);
var result = subject.MethodToBeTested("some-stub-parameter", "businessLogic");
Assert.AreEqual("some expected value", result);

我不知道。若在测试中我验证了该方法调用了另一个方法,那个该怎么办。在当前的方法中,所有的实现也应该调用这个方法。如果所有的实现都做相同的事情,那么我们真正需要这些实现做什么呢?我只是不知道我们可以有什么不同的实现?似乎实现必须完全相同。如果它们是不同的,那么应该测试它们之间的差异。我早期对接口也有这种误解。接口可以满足您所指的目的。当每个具体实现中的实现差异很大时,可以使用它从调用方抽象实现。这也是为什么即使只有一个接口的实现者,它仍然是一个好主意的原因。您可以将具体的实现替换为Leo提到的测试赝品,我的示例如下所示。@name1ess0ne那么您不是单元测试,因为您不应该关心具体对象在其实现中做什么,不管它是否调用一个,单元测试不需要考虑两个或三个方法……当您进行单元测试时,您需要分离/隔离较大对象的复杂性……例如,检查返回值的状态或确保它不会引发异常,或确保它在某些情况下确实引发异常,etcI不知道。如果在测试I验证中