如何在集成测试中模拟装饰类

时间:2018-01-25 15:28:23

标签: .net integration-testing inversion-of-control decorator castle-windsor

我们在项目中使用Castle Windsor。我们有Installer : IWindsorInstaller类,其中包含Install方法以及特定项目的所有注册。

现在我想编写一个集成测试,它将测试所有类的一些“快乐场景”,只使用模拟的数据源。这意味着我想使用模拟数据源而不是生产代码中使用的普通SQL数据源。

问题是由使用Castle Windsor的装饰器支持装饰数据源的事实引起的。这意味着我首先注册装饰器链(按照某种想要的顺序),然​​后注册真实的数据源。集成测试想要调用生产代码的Install方法,但需要放置一个模拟数据源而不是真实数据源。如果没有装饰器,我们可以简单地使用IsDefault来强制容器使用mock而不是最初注册的生产类。但这对于装饰器是不可能的,因为它会覆盖整个装饰器链。

在生产代码中,链条就像:客户端 - >缓存 - >计算 - >数据源

在集成测试中我想要像以下链:客户端 - >缓存 - >计算 - > MockedData

Install中的代码如下所示:

container.Register(Component.For<IRepository, ICacheService<int, List<MyData>>>().ImplementedBy<MyDataCache>());
container.Register(Component.For<IRepository>().ImplementedBy<ComputedColumns>());
container.Register(Component.For<IRepository>().ImplementedBy<DataRepository>());

我现在需要的是跳过最后一次注册。但是如果我在集成测试中编写单独的注册代码,它将不会测试是否正确编写了Install方法。我正在尝试找到一个解决方案,它将与生产代码中的Install方法一起使用。

到目前为止我尝试过:由于我没有找到Castle Windsor的任何真正的解决方案,我将Install方法拆分为两个方法,生产代码调用它们来注册所有内容,但集成测试没有t称那个包含最后一次注册的那个。不幸的是,这个解决方案要求生产代码按特定顺序调用这两个Install方法,否则它不起作用。所以我再次创建了一些危险的代码,这些代码在正常的NUnit测试中是不可测试的。 (这是读取的,我错误地颠倒了那些注册的顺序,直到有人在生产代码中使用它才能看到它...)

1 个答案:

答案 0 :(得分:1)

  

在生产代码中,链条就像:客户端 - &gt;缓存 - &gt;计算    - &GT;数据源

     

在集成测试中我想要像以下链:客户端 - &gt;缓存 - &gt;计算 - &gt; MockedData

从这样的任务表述中,我很清楚Installer应该使用数据源类型(DataSourceMockedData)进行参数化。

由于Installer实现了Castle IWindsorInstaller接口而您无法更改方法Install,因此您可以使Installer类成为具有数据源类型的通用:

public class Installer<TDataRepository> : IWindsorInstaller where TDataRepository : IRepository
{
    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
        container.Register(Component.For<IRepository, ICacheService<int, List<MyData>>>().ImplementedBy<MyDataCache>());
        container.Register(Component.For<IRepository>().ImplementedBy<ComputedColumns>());
        container.Register(Component.For<IRepository>().ImplementedBy<TDataRepository>());
    }
}

现在您可以设置所需的实现,不同的是生产代码和集成测试:

//  In production code
container.Install(new Installer<DataRepository>());

//  In integration test
container.Install(new Installer<MockedRepository>());

这种解决方案没有您所描述的“两种方法”方法的任何缺点。