我尝试对EntitySetController进行单元测试。我可以测试Get但在测试Post方法时遇到问题。
我使用了SetODataPath和SetODataRouteName,但是当我调用this.sut.Post(实体)时,我收到很多关于丢失位置标头,丢失OData-Path,丢失路由的错误。
我在我的智慧结束。 有没有人成功测试他们的EntitySetController?
有人对我有意见吗? 也许我应该只测试我的EntitySetController实现中受保护的覆盖方法?但是我如何测试受保护的方法呢?
感谢您的帮助
答案 0 :(得分:5)
来到这里寻找解决方案。这似乎有效,但不确定是否有更好的方法。
控制器至少需要CreateEntity
和GetKey
覆盖:
public class MyController : EntitySetController<MyEntity, int>
{
protected override MyEntity CreateEntity(MyEntity entity)
{
return entity;
}
protected override int GetKey(MyEntity entity)
{
return entity.Id;
}
}
MyEntity非常简单:
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
}
看起来你至少需要:
+请求带有URI
请求标头中的+ 3个密钥,MS_HttpConfiguration
,MS_ODataPath
和MS_ODataRouteName
+带路由的HTTP配置
[TestMethod]
public void CanPostToODataController()
{
var controller = new MyController();
var config = new HttpConfiguration();
var request = new HttpRequestMessage();
config.Routes.Add("mynameisbob", new MockRoute());
request.RequestUri = new Uri("http://www.thisisannoying.com/MyEntity");
request.Properties.Add("MS_HttpConfiguration", config);
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntity")));
request.Properties.Add("MS_ODataRouteName", "mynameisbob");
controller.Request = request;
var response = controller.Post(new MyEntity());
Assert.IsNotNull(response);
Assert.IsTrue(response.IsSuccessStatusCode);
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
}
我不太确定IHttpRoute
,在aspnet源代码中(我必须链接到这一点来解决这个问题)测试使用这个接口的模拟。因此,对于此测试,我只需创建一个模拟器并实现RouteTemplate
属性和GetVirtualPath
方法。测试期间未使用界面上的所有其他内容。
public class MockRoute : IHttpRoute
{
public string RouteTemplate
{
get { return ""; }
}
public IHttpVirtualPathData GetVirtualPath(HttpRequestMessage request, IDictionary<string, object> values)
{
return new HttpVirtualPathData(this, "www.thisisannoying.com");
}
// implement the other methods but they are not needed for the test above
}
这对我有用,但我对ODataPath
和IHttpRoute
以及如何正确设置我真的不太确定。
答案 1 :(得分:1)
除了@mynameisbob的回答,我发现你也可能需要在Request属性上设置HttpRequestContext:
var requestContext = new HttpRequestContext();
requestContext.Configuration = config;
request.Properties.Add(HttpPropertyKeys.RequestContextKey, requestContext);
我在创建HttpResponseMessage时需要以上添加内容,如下所示:
public virtual HttpResponseException NotFound(HttpRequestMessage request)
{
return new HttpResponseException(
request.CreateResponse(
HttpStatusCode.NotFound,
new ODataError
{
Message = "The entity was not found.",
MessageLanguage = "en-US",
ErrorCode = "Entity Not Found."
}
)
);
}
如果没有设置HttpRequestContext,上面的方法将抛出一个Argument Null Exception,因为CreateResponse扩展方法试图从HttpRequestContext获取HttpConfiguration(而不是直接从HttpRequest获取)。
答案 2 :(得分:1)
确定更新的答案。
我还发现支持成功执行返回的IHttpActionResult,还需要做一些事情。
这是我到目前为止找到的最佳方法,我确信有更好的方法,但这对我有用:
// Register OData configuration with HTTP Configuration object
// Create an ODataConfig or similar class in App_Start
ODataConfig.Register(config);
// Get OData Parameters - suggest exposing a public GetEdmModel in ODataConfig
IEdmModel model = ODataConfig.GetEdmModel();
IEdmEntitySet edmEntitySet = model.EntityContainers().Single().FindEntitySet("Orders");
ODataPath path = new ODataPath(new EntitySetPathSegment(edmEntitySet));
// OData Routing Convention Configuration
var routingConventions = ODataRoutingConventions.CreateDefault();
// Attach HTTP configuration to HttpRequestContext
requestContext.Configuration = config;
// Attach Request URI
request.RequestUri = requestUri;
// Attach Request Properties
request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey, config);
request.Properties.Add(HttpPropertyKeys.RequestContextKey, requestContext);
request.Properties.Add("MS_ODataPath", path);
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
request.Properties.Add("MS_EdmModel", model);
request.Properties.Add("MS_ODataRoutingConventions", routingConventions);
request.Properties.Add("MS_ODataPathHandler", new DefaultODataPathHandler());
答案 3 :(得分:0)
此外,要获得正确的Location头值等,您真的想要调用Web Api应用程序OData配置代码。
所以而不是使用:
config.Routes.Add("mynameisbob", new MockRoute());
您应该将设置OData路由的WebApiConfig类的部分分隔为单独的类(例如ODataConfig),并使用它来为您的测试注册正确的路由:
e.g。
ODataConfig.Register(config);
您需要注意的唯一事项是以下行符合您的路由配置:
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntity")));
request.Properties.Add("MS_ODataRouteName", "mynameisbob");
因此,如果您的Web API OData配置如下:
config.Routes.MapODataRoute("ODataRoute", "odata", GetEdmModel());
private static IEdmModel GetEdmModel()
{
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<MyEntity>("MyEntities");
IEdmModel model = modelBuilder.GetEdmModel();
return model;
}
然后这是正确的配置:
request.Properties.Add("MS_ODataPath", new ODataPath(new EntitySetPathSegment("MyEntities")));
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
有了这个,您的位置标题将正确生成。
答案 4 :(得分:0)
除了这里的所有内容之外,我还必须手动将上下文附加到请求,以及创建路由数据。不幸的是,我没有办法在不依赖路由/模型配置的情况下进行单元测试。
因此使用名为“ODataRoute”的路由,这是我的静态ODataConfig.Configure()
方法中建立的正常配置的一部分(与上面相同,它创建模型并调用一堆MapODataServiceRoute
),以下代码用于为测试准备控制器:
protected static void SetupControllerForTests(ODataController controller,
string entitySetName, HttpMethod httpMethod)
{
//perform "normal" server configuration
var config = new HttpConfiguration();
ODataConfig.Configure(config);
//set up the request
var request = new HttpRequestMessage(httpMethod,
new Uri(string.Format("http://localhost/odata/{0}", entitySetName)));
//attach it to the controller
//note that this will also automagically attach a context to the request!
controller.Request = request;
//get the "ODataRoute" route from the configuration
var route = (ODataRoute)config.Routes["ODataRoute"];
//extract the model from the route and create a path
var model = route.PathRouteConstraint.EdmModel;
var edmEntitySet = model.FindDeclaredEntitySet(entitySetName);
var path = new ODataPath(new EntitySetPathSegment(edmEntitySet));
//get a couple more important bits to set in the request
var routingConventions = route.PathRouteConstraint.RoutingConventions;
var pathHandler = route.Handler;
//set the properties of the request
request.SetConfiguration(config);
request.Properties.Add("MS_ODataPath", path);
request.Properties.Add("MS_ODataRouteName", "ODataRoute");
request.Properties.Add("MS_EdmModel", model);
request.Properties.Add("MS_ODataRoutingConventions", routingConventions);
request.Properties.Add("MS_ODataPathHandler", pathHandler);
//set the configuration in the request context
var requestContext = (HttpRequestContext)request.Properties[HttpPropertyKeys.RequestContextKey];
requestContext.Configuration = config;
//get default route data based on the generated URL and add it to the request
var routeData = route.GetRouteData("/", request);
request.SetRouteData(routeData);
}
这让我花了几天时间拼凑起来,所以我希望这至少可以拯救其他人。