从容器测试接口是好还是坏?

时间:2014-07-15 02:28:00

标签: c# unit-testing

请帮助我澄清以下设计方法: 在我们使用UnityContainer的单元测试项目中。在每个测试的开始,我们正在解析一个对象,通过该容器进行测试:

IObjectToTest objectToTest = container.Resolve<IObjectToTest>();

在Unity配置中,我们配置了IObjectToTest的具体实现。 在测试中,我们测试了这个接口IObjectToTest objectToTest,尽管该对象的具体实例。

问题是:这种方法的优点和缺点是什么?为什么我们不能实例化新的ObjectToTest(具体实现)并测试它?为什么我们使用Unity和界面?

2 个答案:

答案 0 :(得分:3)

嗯,在这种方法中没有什么奇怪的,实际上它比你想象的更常见。当你想编写可测试的代码时,你会发现自己使用了大量的依赖注入。

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

答案 1 :(得分:1)

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

如果您测试具有被测对象的依赖类的具体实现,那么您不是真正测试单元,而是集成。最终,集成在生产代码中非常重要,但如果对各个单元进行彻底和正确的测试,您无需担心。

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

在下面的示例中,我们测试IObjectUnderTest的行为,而不考虑IBusinessLogicObject的实现。我们通过使用Moq框架模拟依赖项来验证两个对象之间的协作。通过这样做,我们简单地验证了两个对象之间发生了必要的协作,并且不关心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);

实施例

public ObjectUnderTest : IObjectUnderTest
{
    private readonly IBusinessLogicObject businessLogicObject;
    public ObjectUnderTest(IBusinessLogicObject businessLogicObject)
    {
        businessLogicObject = businessLogicObject;
    }
    string IObjectUnderTest.MethodToBeTested(string businessLogicParam, string someOtherParam)
    {
        if ( someOtherParam == "businessLogic")
        {
            return businessLogicObject.SomeMethod(businessLogicParam);
        }
        return string.Empty;
    }
}