【问题标题】:How can i unit test an EntitySetController我如何对 EntitySet 控制器进行单元测试
【发布时间】:2013-05-31 21:59:24
【问题描述】:

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

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

我无计可施。 有没有人成功地测试了他们的 EntitySetController?

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

感谢您的帮助

【问题讨论】:

    标签: asp.net asp.net-web-api odata


    【解决方案1】:

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

    控制器至少需要 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      
    }
    

    这对我有用,但是我真的不太确定 ODataPathIHttpRoute 以及如何正确设置它。

    【讨论】:

    • 感谢您的回答。对我来说看起来不错。与此同时,我通过调用受保护的方法对我的 OData-Controller 进行单元测试,并相信来自 Web.API 团队和我的集成测试的正确实现。
    【解决方案2】:

    除了@mynameisbob 的回答,我发现您可能还需要在请求属性上设置 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。

    【讨论】:

      【解决方案3】:

      此外,要获得正确的 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 标头将正确生成。

      【讨论】:

        【解决方案4】:

        确定更新答案。

        我还发现支持成功执行返回的 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());
        

        【讨论】:

          【解决方案5】:

          除了这里的所有内容之外,我还必须手动将上下文附加到请求中,以及创建路由数据。不幸的是,如果不依赖路由/模型配置,我就无法进行单元测试。

          所以使用名为“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);
          }
          

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

          【讨论】:

            猜你喜欢
            • 2011-07-26
            • 2012-04-24
            • 2018-09-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多