我已经使用Ninject设置了一个Web API项目,并且我使用了详细的here修复程序来使其与最新版本的Web API一起使用。一切都很好,但我现在正在尝试写一些测试。
我正在使用内存中托管来运行测试项目,如详细here,因为我有DelegatingHandler
执行身份验证,然后在请求消息上设置属性所有Api控制器使用。
所以,我的测试有一个基类,我有一个SetUp
方法设置HttpServer
和配置,我几乎从我的工作Ninject代码中取出:
[SetUp]
public void Setup()
{
bootstrapper = new Bootstrapper();
DynamicModuleUtility.RegisterModule(
typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(
typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
var config = new HttpConfiguration();
config.Routes.MapHttpRoute("Login",
"api/auth/token",
new { controller = "Users", action = "Login" });
config.IncludeErrorDetailPolicy =
IncludeErrorDetailPolicy.Always;
config.DependencyResolver =
new NinjectResolver(CreateKernel());
config.MessageHandlers.Add(
new AuthenticationHandler(CreateUserManager()));
Server = new HttpServer(config);
}
这就是我创建MoqMockingKernel
:
private static IKernel CreateKernel()
{
var kernel = new MoqMockingKernel();
kernel.Bind<Func<IKernel>>()
.ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>()
.To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
GlobalConfiguration.Configuration.DependencyResolver =
new NinjectResolver(kernel);
return kernel;
}
这就是我注册要使用的对象的方式:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IUserManager>().ToMock();
kernel.Bind<UsersController>().ToSelf();
}
虽然我本身并没有测试Controller,但我确实希望调用它的正确实例,这就是我将它绑定到ToSelf的原因。我必须承认我认为这是正确的。这是一个测试的例子:
public void UserCannotLogin()
{
System.Net.Http.HttpClient client =
new System.Net.Http.HttpClient(Server);
string json = string.Format(
"{{ \"Username\": \"{0}\", \"Password\": \"{1}\" }}",
"wrong", "wrong");
HttpRequestMessage request =
CreateRequest(@"api/auth/token", json, HttpMethod.Get);
Action action = () => client.SendAsync(request);
using (var response = client.SendAsync(request).Result)
{
response.StatusCode.Should()
.Be(HttpStatusCode.Unauthorized);
}
}
我基本上得到了404错误。当我调试它时,它会转到我的DelegatingHandler
,但它不会转到我的控制器。
我觉得我从根本上忽略了这一点,甚至可能无法做我想做的事情,但如果有人对如何做到这一点有任何建议,或者不同实现同样的目的的方式,我都是耳朵。
更新我认为这是因为MockingKernel
的默认行为是提供一个Mock,除非另有说明,所以它返回的模拟IHttpControllerSelector
。我现在已经设置了几个默认值:
kernel.Bind<IHttpControllerSelector>()
.To<DefaultHttpControllerSelector>();
kernel.Bind<IContentNegotiator>()
.To<DefaultContentNegotiator>();
它仍然无效,我认为因为没有指定格式化程序。我明天会试试,看看能不能让我到那儿。
答案 0 :(得分:1)
好吧,当我说我从根本上忽略了一点时,我认为我是正确的,但我会回答这个问题,以防其他人避免同样的错误!
我认为Ninject MockingKernel
主要是关于自动模拟,所以如果你有很多接口,你不关心它们在你的测试中的设置方式,你可以忽略它们测试,它们将自动为您创建。
对于Web API,这绝对是 not 的情况,因为您不希望自动模拟控制器选择器类,否则您不会最终调用您的控制器。
所以,我提出的解决方案是坚持使用标准的Ninject Kernel
,然后将你的接口绑定到一个常量的Mock对象:
kernel.Bind<IUserManager>().ToConstant(CreateUserManager());
private IUserManager CreateUserManager()
{
Mock<IUserManager> userManager = new Mock<IUserManager>();
// Set up the methods you want mocked
return userManager.Object;
}
这样做,我已经能够成功编写使用HttpClient的测试来调用内存中的HttpServer,该HttpServer成功调用DelegatingHandler
然后最终到达我的控制器。