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