测试Web Api模型绑定器

时间:2017-12-29 18:14:16

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

我有一个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创建值提供程序

2 个答案:

答案 0 :(得分:1)

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类型参数是要测试的绑定器的类型。
  • TModel类型参数是您的对象的预期类型。
  • modelName参数是查询字符串参数的 name (可以是任何值)。
  • queryString是组成的查询字符串,其中包含modelName指定的参数。它必须匹配!
  • 活页夹参数是您的活页夹的一个实例。

此外,如果您对测试错误条件感兴趣(应该如此),请将其功能的最后一行更改为:

     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 :(得分:0)

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

a = [2, 4, 1, 3, 4, 5, 1, 5, 7, 8, 2, 4,]

如果你想让它适用于post你接受一个jsonValues字符串修改httpControllerContext就像这样:

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

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