假设我有一个需要静态属性的方法,我想创建单元测试,所以我将它包装在一个包装类中。我们称之为接口IFoo&具体课堂Foo。
现在如果从MVC视图中调用我的方法,那么如何将包装器实例放入该方法?
显然,我可以将IFoo参数添加到我的控制器构造函数中,将IFoo属性添加到我的视图模型中,并将IFoo参数添加到我的方法中,然后将其传递给链;控制器,视图模型,视图,扩展方法。这似乎不适合我。
那么有更简洁的方法吗?
我假设DI容器是要走的路。老实说,到目前为止我还没需要一个,我天真地假设我只是添加Ninject,将具体类型绑定到接口,并在我的方法中进行以下调用。
var dt = kernel.Get<IFoo>();
我认为这可以帮助我避免上面提到的整个构造函数参数/属性跟踪。现在我知道我仍然需要从某个地方获取内核变量,但我想我还记得看到一些关于使用线程/会话/请求范围调用它的东西。我想我可以实例化内核的相同实例,无论它在何处被调用,但是当我查看它时,我发现只有内核调用的对象实例...而不是内核本身。
那么,有没有办法让Foo的实例进入方法而不通过一堆除了通过它之外什么都不做的对象?
答案 0 :(得分:1)
由于我不知道您的确切用例,我可能建议的可能适合也可能不适合。我有几种方法可以解决这个问题,这取决于你在哪里测试。从扩展方法的角度来看,我认为您可以使用DateTime
并在单元测试中为扩展手动注入适当的值。如果它需要针对 DateTime.UtcNow
测试值,那就有点不同了,但你可以使用可以为空的DateTime
处理它,默认为DateTime.UtcNow
如果你不知道t在第二个参数中供电。
public static IHtmlString RegularExtension(this HtmlHelper helper, DateTime when)
{
...
}
public static IHtmlString ComparisonExtension(this HtmlHelper helper, DateTime when, DateTime? now = null)
{
var nowDate = now ?? DateTime.UtcNow; // verified by inspection, tests use specified values
...
}
或者,对于后者,如果您对null合并运算符感到不舒服,请使用两种方法。采用单个参数的公共参数和使用两者的私有参数。使用反射测试您的私有方法,让公共方法简单地委托给私有方法。您也可以考虑使用单个方法,其中两个参数都是必需的,并且始终将两个值都构建到模型中。
从控制器的角度来看,如果需要对当前时间使用一致的值,可以将包装器注入控制器,并使用包装器中的“解包”值填充模型。这样,您可以测试控制器是否正确设置了模型上的值,而无需将所有内容绑定到包装器。关键是所有代码都使用注入(和解包)值而不是直接调用DateTime.UtcNow
。从对这些类的测试的角度来看,他们不知道价值来自何处,只是它是从上游提供的。
public FooController(IDateTimeWrapper timeWrapper)
{
var model = new FooModel { Now = timeWrapper.Unwrap(), ... };
...
return View(model);
}
答案 1 :(得分:1)
如果你的ViewModel或View也是由Ninject创建的,你应该能够将IFoo作为构造函数依赖项添加并注入它。
如果您想直接访问Kernel
并解析一个实例(这通常不是最好的主意),您可以注入一个IResolutionRoot
并调用Get on。
答案 2 :(得分:1)
首先,购买http://manning.com/seemann - 你不会后悔的。
一般来说,“我在我的对象树中,给我X”的问题可通过以下方式之一解决:
Func<T>
或interface IXFactory { X CreateX(); }
- 请参阅Ninject.Extensions.Factory wiki 你期待容器的大部分容易让任何人都可以神奇地尖叫,“我想要X”,特别是在从DTO调用的静态类中。请记住,容器不会进入并重写您的IL interceptign调用new。在将无瑕疵的布线工作在一起工作时,基本上可以使用一些装饰器,工厂和代理作为粘合剂。
最重要的是,处理这样的事情的适当方式是
最后一点至关重要 - 通过显示真实的依赖关系并聆听它们,您最终会找出时间服务,调度程序或格式化程序(或者您的案例中缺少的更高级别抽象)应该在哪里生存时间。
我强烈建议您在这里阅读Mark Seemann's top answers,因为大多数这些重要问题和主题都会在其中得到深入探讨,而不会像我的回答那样依赖过于简单的解释。当然,这本书本身就是你对时间和金钱的最佳总体利用!
编辑:(灵感来自你对@tvonfosson的评论)一个容器可以带给派对的一个关键因素(除了在给定范围内维护单个X(参见Ninject.Extensions.NamedScope wiki)之外,还能够跳过无理由地将间接依赖关系传递给服务层次结构 - 例如,如果您的Controller依赖于依赖于调度程序的服务并且调度程序需要时钟,则服务不需要讨论时钟,只需要调度程序。容器的关键是确保你不要走太远,然后尝试滥用它们代码应该只是代码。