【问题标题】:Testing Web Api Model Binders测试 Web Api 模型绑定器
【发布时间】:2018-06-10 03:13:19
【问题描述】:

我有一个 Web Api Model Binder,如下所示:

public class KeyModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {  
     //...
    }
}

我正在尝试编写规则以使其更易于测试。我找到了一个可以与 MVC 的模型绑定器 here 一起使用的函数:

但在尝试转换为使用 webApi 时,我无法弄清楚如何填充值提供者

    public TModel BindModel<TBinder, TModel>(NameValueCollection formCollection, TBinder binder)
        where TBinder : IModelBinder
    {
        var valueProvider = new NameValueCollectionValueProvider(formCollection, null);

        var dataProvider = new DataAnnotationsModelMetadataProvider();
        var modelMetadata = dataProvider.GetMetadataForType(null, typeof(TModel));

        var bindingContext = new ModelBindingContext
        {
            ModelName = typeof(TModel).Name,
            ValueProvider = valueProvider,
            ModelMetadata = modelMetadata
        };

        binder.BindModel(null, bindingContext);
        return (TModel)bindingContext.ModelMetadata.Model;
    }

NameValueCollection 仅存在于 MVC 中,如何为 Web-Api 创建值提供者

【问题讨论】:

    标签: c# asp.net-mvc unit-testing asp.net-web-api model-binding


    【解决方案1】:

    不使用默认值提供程序无法测试模型绑定器。所以我根据预期的规则编写了我的绑定模型。在这种情况下,我只需要测试gets

    public TModel BindModelFromGet<TBinder, TModel>(string modelName, string queryString, TBinder binder)
        where TBinder : IModelBinder
    {
        var httpControllerContext = new HttpControllerContext();
        httpControllerContext.Request = new HttpRequestMessage(HttpMethod.Get, MOCK_URL + queryString);
        var bindingContext = new ModelBindingContext();
    
        var dataProvider = new DataAnnotationsModelMetadataProvider();
        var modelMetadata = dataProvider.GetMetadataForType(null, typeof(TModel));
    
        var httpActionContext = new HttpActionContext();
        httpActionContext.ControllerContext = httpControllerContext;
    
        var provider = new QueryStringValueProvider(httpActionContext, CultureInfo.InvariantCulture);
    
        bindingContext.ModelMetadata = modelMetadata;
        bindingContext.ValueProvider = provider;
        bindingContext.ModelName = modelName;
    
        if (binder.BindModel(httpActionContext, bindingContext))
        {
            return (TModel)bindingContext.Model;
        }
    
        throw new Exception("Model was not bindable");
    }
    

    如果您希望这适用于帖子,您可以使用 jsonValues 字符串修改 httpControllerContext,如下所示:

    httpControllerContext.Request = new HttpRequestMessage(HttpMethod.Post, "");
    httpControllerContext.Request.Content = new ObjectContent<object>(jsonValues, new JsonMediaTypeFormatter());
    

    那么您只需要使用正确的 ValueProvider(我没有研究如何使用它,因为我不需要它)。

    【讨论】:

      【解决方案2】:

      johnny 5 的答案是正确的,但是,至少对我来说,很难理解如何使用它......所以......考虑这个问题的答案,它引入了我们想要进行单元测试的活页夹:

      Passing UTC DateTime to Web API HttpGet Method results in local time

      这只是将所有日期时间转换为 DateTimeKind.Utc。要对其进行单元测试,我们需要一些虚假的 URI(不一定是真实的):

          const string MOCK_URL = "http://localhost/api/george";
      

      然后使用他的 BindModelFromGet 方法进行单元测试,如下所示:

          [Test]
          public void should_convert_datetime_to_utc()
          {
              var bar = new UtcDateTimeModelBinder();
              var dateTime = BindModelFromGet<UtcDateTimeModelBinder, DateTime>
                  ("fred", "?fred=2019-08-12 00:00:00Z", bar);
              Assert.That(dateTime.Kind, Is.EqualTo(DateTimeKind.Utc));
          }
      

      注意事项:

      • TBinder type 参数是要测试的 binder 的 type 类型。
      • TModel 类型参数是您的对象的预期类型。
      • modelName 参数是您的查询字符串参数的名称(可以是任何值)。
      • queryString 是一个组成的查询字符串,其中包括由 modelName 指定的参数。它必须匹配!
      • binder 参数是您的 binder 的一个实例。

      另外,如果你有兴趣测试错误条件(你应该是),把他函数的最后一行改成:

           throw new Exception(bindingContext.ModelState[modelName].Errors[0].ErrorMessage);
      

      并像这样测试:

          [Test]
          public void should_handle_bad_dates()
          {
              var bar = new UtcDateTimeModelBinder();
              var ex = Assert.Throws<Exception>(() => BindModelFromGet<UtcDateTimeModelBinder, DateTime>
                  ("fred", "?fred=NotADate", bar));
              Assert.That(ex.Message, Is.EqualTo("Cannot convert value to Utc DateTime"));
          }
      

      这将处理简单的验证问题,但请注意,如果活页夹添加了多个错误或通常不像简单的日期转换器那样工作,您将需要做更多的工作。

      【讨论】:

      • 很好,+1 澄清
      猜你喜欢
      • 2012-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多