3

我尝试对 EntitySetController 进行单元测试。我可以测试 Get 但在测试 Post 方法时遇到问题。

我玩过 SetODataPath 和 SetODataRouteName 但是当我调用 this.sut.Post(entity) 时,我收到很多关于缺少位置标题、缺少 OData-Path、缺少路由的错误。

我已经黔驴技穷了。有没有人成功地测试了他们的 EntitySetController?

有人对我有想法吗?也许我应该只测试我的 EntitySetController 实现中受保护的覆盖方法?但是我如何测试受保护的方法?

谢谢你的帮助

4

5 回答 5

5

也来这里寻找解决方案。这似乎有效,但不确定是否有更好的方法。

控制器需要最少CreateEntityGetKey覆盖:

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_HttpConfigurationMS_ODataPathMS_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如何正确设置它。

于 2013-06-05T14:53:01.967 回答
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 异常,因为 CreateResponse 扩展方法尝试从 HttpRequestContext(而不是直接从 HttpRequest)获取 HttpConfiguration。

于 2013-09-14T04:21:39.103 回答
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());
于 2013-09-19T05:50:03.577 回答
0

此外,要获得正确的 Location 标头值等,您确实需要调用 Web Api 应用程序 OData 配置代码。

所以而不是使用:

config.Routes.Add("mynameisbob", new MockRoute());

您应该将设置 OData 路由的 WebApiConfig 类的部分分离到一个单独的类(例如 ODataConfig)中,并使用它为您的测试注册正确的路由:

例如

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");

有了这个,您的 Location 标头将正确生成。

于 2013-09-14T06:08:44.767 回答
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);
}

这花了我几天的大部分时间来拼凑,所以我希望这至少可以拯救其他人。

于 2015-12-07T05:33:12.937 回答