C# 测试webapi模型绑定

C# 测试webapi模型绑定,c#,asp.net-mvc,unit-testing,asp.net-web-api,model-binding,C#,Asp.net Mvc,Unit Testing,Asp.net Web Api,Model Binding,我有一个Web Api模型活页夹,看起来是这样的: public class KeyModelBinder : IModelBinder { public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { //... } } 我正试图写一个规则,使它更容易测试。我发现了一个与MVC的模型绑定器一起工作的函数: 但是,当尝试转换为使用we

我有一个Web Api模型活页夹,看起来是这样的:

public class KeyModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {  
     //...
    }
}
我正试图写一个规则,使它更容易测试。我发现了一个与MVC的模型绑定器一起工作的函数:

但是,当尝试转换为使用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;
    }
公共TModel BindModel(NameValueCollection formCollection,TBinder binder) 其中TBinder:IModelBinder { var valueProvider=新名称ValueCollectionValueProvider(formCollection,null); var dataProvider=新的DataAnnotationsModelMetadataProvider(); var modelMetadata=dataProvider.GetMetadataForType(null,typeof(TModel)); var bindingContext=新模型bindingContext { ModelName=typeof(TModel).Name, ValueProvider=ValueProvider, ModelMetadata=ModelMetadata }; binder.BindModel(null,bindingContext); 返回(TModel)bindingContext.ModelMetadata.Model; }
NameValueCollection仅存在于MVC中,如何为Web Api创建一个值提供程序?如果不使用默认值提供程序,则测试模型绑定器是不可能的。因此,我根据预期的规则编写了绑定模型。在这种情况下,我只需要测试GET

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");
}
publictmodelbindmodelfromget(字符串modelName、字符串queryString、TBinder binder)
其中TBinder:IModelBinder
{
var httpControllerContext=新的httpControllerContext();
httpControllerContext.Request=新的HttpRequestMessage(HttpMethod.Get,MOCK_URL+queryString);
var bindingContext=新的ModelBindingContext();
var dataProvider=新的DataAnnotationsModelMetadataProvider();
var modelMetadata=dataProvider.GetMetadataForType(null,typeof(TModel));
var httpActionContext=新的httpActionContext();
httpActionContext.ControllerContext=httpControllerContext;
var provider=新的QueryStringValueProvider(httpActionContext,CultureInfo.InvariantCulture);
bindingContext.ModelMetadata=ModelMetadata;
bindingContext.ValueProvider=提供程序;
bindingContext.ModelName=ModelName;
if(binder.BindModel(httpActionContext,bindingContext))
{
return(TModel)bindingContext.Model;
}
抛出新异常(“模型不可绑定”);
}
如果您想让这一点在post中起作用,请输入一个jsonValues字符串,修改httpControllerContext,如下所示:

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

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

johnny 5的答案是正确的,但是,至少对我来说,很难理解如何使用它。。。所以考虑这个问题的答案,它介绍了一个我们想进行单元测试的绑定器:

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

然后使用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));
    }
并进行如下测试:

    [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"));
    }
[测试]
public void应该处理错误日期()
{
var bar=新的UtcDateTimeModelBinder();
var ex=Assert.Throws(()=>BindModelFromGet
(“弗雷德”,“弗雷德=NotADate”,bar));
Assert.That(例如Message,Is.EqualTo(“无法将值转换为Utc日期时间”);
}

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

Nice,+1用于澄清
     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"));
    }