我有一个MVC项目,其标准IoC设置使用StructureMap将存储库注入控制器构造函数。
我还决定要有一个静态的“实用程序”类,我可以使用不同控制器调用的常用方法。例如,我有:
public static IEnumerable<CountryCode> GetCountryList()
{
ICountryCodeRepository repo = ObjectFactory.GetInstance<ICountryCodeRepository>();
IEnumerable<CountryCode> countries = repo.GetAll();
return countries;
}
如您所见,它直接从ObjectFactory创建一个repo对象。现在的问题是,当我想对我的控制器进行单元测试时,我可以模拟控制器的存储库,但不能模拟实用程序类中的存储库(控制器最终调用它)我确定还有其他原因我的实用程序类是错误的,但这是我到目前为止看到的。我还读了一些stuff说我拥有的是bad design,我只是不知道如何解决它。
我在考虑让GetCountryList()函数接受一个repo对象
GetCountryList(ICountryCodeRepository _repo)
并且调用控制器会将其传入,但这不仅仅是创建另一个依赖关注点,因为所有控制器都必须知道实用程序函数需要什么?
或者是否可以以某种方式使用StructureMap来注入这些实用方法?
答案 0 :(得分:2)
至少可以,你知道你正在做的是糟糕的设计。这很好,阅读这篇文章的人也会知道这一点并避免和你做同样的错误。
但现在,您可以在静态类中使用提供程序:
public static class Foo
{
public static Func<ICountryCodeRepository> CountryRepoProvider =
() => ObjectFactory.GetInstance<ICountryCodeRepository>();
public static IEnumerable<CountryCode> GetCountryList()
{
return CountryRepoProvider().GetAll();
}
}
现在在你的单元测试中你可以嘲笑它:
Foo.CountryRepoProvider = () => mocha;
或者如果您使用的是ASP.NET MVC 3并且您的DI框架使用dependency resolver,您可以通过至少使其与DI框架无关来改进此代码:
public static IEnumerable<CountryCode> GetCountryList()
{
var repo = DependencyResolver.Current.GetService<ICountryCodeRepository>();
return repo.GetAll();
}
现在,在您的单元测试中,您当然可以编写一个自定义依赖项解析器,它会吐出您服务的模拟实例。
现在当你看到这段代码时,你可能真的对自己说:我在做什么? 我正在编写一个静态类,其中一个衬管方法委托给我从DI获取的存储库。当我可以从我的DI框架中直接将该存储库的实例注入到我需要的地方然后简单地调用我需要的方法时,有什么意义呢?在那些单线性静态方法中,我的单元测试是什么?我为什么要浪费时间。
当然,如果你有更复杂的逻辑来处理你,你只需编写一个服务层,它将把必要的存储库作为构造函数依赖,并对它们执行复杂的业务操作。然后,您将简单地配置您的DI框架,以便在您的控制器或任何需要的地方注入准备好使用的服务实例。看到?不需要任何静态类。弱耦合和单元测试友好代码。