这主要是一个嘲弄问题。
我正在尝试编写自动Web API
测试,并且无法使用外部定义的IHttpControllerActivator
在测试环境中激活我的控制器。
我可以激活控制器,如果我模拟IHttpControllerActivator并通过ControllerActivator.Create
通过指定它们的具体类型显式连接类型控制器的实例,但这需要我在预定义的合成空间之外双重实现我的合成根:
例如......
var dispatcher = new Moq.Mock<IHttpControllerActivator>();
Type controllerType = typeof(MyController);
repositories.BazingaRepository bazinga = new repositories.BazingaRepository
(new entities.BazingaEntities());
MyController controller = new MyController(bazinga);
dispatcher.Setup(context => context.Create(
Moq.It.IsAny<HttpRequestMessage>()
,Moq.It.IsAny<HttpControllerDescriptor>()
,controllerType)).Returns(controller);
dispatcher.As<IDisposable>().Setup(disposable=>disposable.Dispose());
return dispatcher.Object;
会工作,但由于与我的问题没有密切关系的几个原因,这项技术是不受欢迎的。
切入正确的追踪,下面的Root = () =>{...}
代码满足构建要求,但无法在运行时激活我的控制器。更具体地说,我无法拨打Create(...)
来传递给compositionroot.Create(...)
。
compositionroot.Create(...)
激活以及如何解决问题的原因的见解吗?using my.system.resource.providers;
using Microsoft.Owin.Hosting;
using Moq;
using Newtonsoft.Json;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using TechTalk.SpecFlow;
namespace my.system.resource.specs
{
[Binding]
public class Steps
{
private delegate IHttpControllerActivator Composition();
private Composition Root;
[When]
public void When()
{
Root = () =>
{
//+=================================================================+
//|I first tried this out by "just constructing" a |
//|new [my.system.composition.CompositionRoot] |
//|and using it as my dispatcher, since it quite naturally *is* |
//|an IHttpControllerActivator, but I could not get any request |
//|to hit the Activator at all. At that point, I thought that it |
//|might be possible to build the CompositionRoot, but attach it |
//|behind a Mock acting as a proxy for the test (since I know that |
//|mocking a Setup of Create(...) actually will work with a Returns)|
//|So, I am trying to declare that "all of the paramaters to |
//|the Dispatcher are Do-Not-Cares. Just pipe them over to |
//|compositionroot's Create(...) method. |
//|The code below satisfies all of the build-time requirements,but |
//|fails to Activate at run-time. My error coming out of the system|
//|is "No HTTP resource was found that matches the request URI" |
//+=================================================================+
var compositionroot = new my.system.composition.CompositionRoot();
var dispatcher = new Moq.Mock<IHttpControllerActivator>();
//+=====================================================+
//|[Edit] |
//|when I switch It.IsAny<Type> for my |
//|actual controller types in the Setup |
//|below, compositionroot.Create is called |
//|but the controller is not returned. |
//+-----------------------------------------------------+
//|[Alt]Any advice on how I can get the IHttpController |
//|To be returned as the active controller to my client?|
//+=====================================================+
dispatcher.Setup(dispatch =>
dispatch.Create(It.IsAny<HttpRequestMessage>()
, It.IsAny<HttpControllerDescriptor>()
, It.IsAny<Type>()))
.Callback((HttpRequestMessage request
, HttpControllerDescriptor descriptor
, Type type)
=> compositionroot.Create(request
, descriptor
, type));
dispatcher.As<IDisposable>().Setup(disposable => disposable.Dispose());
return dispatcher.Object;
};
}
[Then]
public void Then()
{
Startup startup = new Startup();
Hosting.Options options = new Hosting.Options();
options.Dispatcher = Root();
startup.Register = () => { return options; };
using (WebApp.Start(url: "http://localhost:9000", startup: startup.Configuration))
using (var client = new System.Net.Http.HttpClient())
{
Uri resource = new Uri("http://locahost:9000/Resource/Time;now");
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (HttpResponseMessage response = client.GetAsync(resource).Result)
{
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
var error = response.Content.ReadAsStringAsync().Result;
Assert.Fail(error);
}
}
}
}
}
}
my.resource.specs.Startup
是:
public class Startup
{
public delegate MySystemStartupOptions Registration();
public void Configuration(IAppBuilder app)
{
HttpConfiguration configuration = new HttpConfiguration();
PricingEngineOptions registration = Register();
configuration.MapHttpAttributeRoutes();
configuration.EnableCors();
configuration.Formatters.Clear();
configuration.Formatters.Add(new XmlMediaTypeFormatter());
configuration.Formatters.Add(new JsonMediaTypeFormatter());
configuration.Formatters.XmlFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
configuration.Formatters.XmlFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
configuration.Formatters.JsonFormatter.SerializerSettings.Converters.Add(new StringEnumConverter());
configuration.Services.Replace(typeof(IHttpControllerActivator), registration.Dispatcher);
app.UseWebApi(configuration);
return app;
}
public Registration Register;
}
public class MySystemStartupOptions
{
public IHttpControllerActivator Dispatcher {get;set;}
}
my.system.resource.composition
public partial class CompositionRoot : IHttpControllerActivator
{
public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
if(controllerType == typeof(my.system.resource.controllers.TimeController))
{
var controller = new my.system.resource.controllers.TimeController();
return controller;
}
}
}
my.system.resource.controllers
public class TimeController : ApiController
{
[HttpGet]
[Route("Resource/Time;now")]
public HttpResponseMessage HttpGetCurrentTime()
{
DateTime now = DateTime.Now;
return Request.CreateResponse(HttpStatusCode.OK, now);
}
}
编辑以按要求添加包列表和重点项目结构: 涉及4个相关项目
my.system.resource.specs
my.system.resource.composition
my.system.resource.controllers
my.system.resource.providers
NuGet包
EntityFramework
->my.system.resource.specs|5.0.0
HtmlAgilityPack
->my.system.resource.controllers|1.4.9
Microsoft.AspNet.Cors
->my.system.resource.specs|5.2.3
->my.system.resource.controllers|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi
->my.system.resource.specs|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi.Cors
->my.system.resource.specs|5.2.3
->my.system.resource.controllers|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi.Client
->my.system.resource.specs|5.2.3
->my.system.resource.controllers|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi.Core
->my.system.resource.specs|5.2.3
->my.system.resource.controllers|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi.Owin
->my.system.resource.specs|5.2.3
Microsoft.AspNet.WebApi.WebHost
->my.system.resource.specs|5.2.3
->my.system.resource.composition|5.2.3
Microsoft.AspNet.WebApi.OwinSelfHost
->my.system.resource.specs|5.2.3
Microsoft.Owin.Host.HttpListener
->my.system.resource.specs|2.0.2
Microsoft.Web.Infrastructure
->my.system.resource.specs|1.0.0
Microsoft.Owin
->my.system.resource.specs|2.0.2
Newtonsoft.Json
->my.system.resource.specs|6.0.4
->my.system.resource.controllers|6.0.4
->my.system.resource.composition|6.0.4
->my.system.resource.providers|6.0.4
Microsoft.Owin.Hosting
->my.system.resource.specs|2.0.2
Owin
->my.system.resource.specs|1.0.0
Moq
->my.system.resource.specs|4.2.1510.22
->***my.system.resource.composition|4.2.1510.22
SpecFlow
->my.system.resource.specs|2.0.0
SpecFlow.NUnit
->my.system.resource.specs|2.0.0
NUnit
->my.system.resource.specs|3.0.0
StackExchange.Redis
->my.system.resource.providers|1.0.488
->my.system.resource.controllers|1.0.488