我们在项目中使用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测试中是不可测试的。 (这是读取的,我错误地颠倒了那些注册的顺序,直到有人在生产代码中使用它才能看到它...)
答案 0 :(得分:1)
在生产代码中,链条就像:客户端 - &gt;缓存 - &gt;计算 - &GT;数据源
在集成测试中我想要像以下链:客户端 - &gt;缓存 - &gt;计算 - &gt; MockedData
从这样的任务表述中,我很清楚Installer
应该使用数据源类型(DataSource
或MockedData
)进行参数化。
由于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>());
这种解决方案没有您所描述的“两种方法”方法的任何缺点。