【问题标题】:How to unit test modelbinder with ModelMetadata如何使用 ModelMetadata 对模型绑定器进行单元测试
【发布时间】:2011-02-18 06:45:51
【问题描述】:

如何对自定义 ModelBinder 进行单元测试?

这是代码。

public class MagicBinder : DefaultModelBinder
    {

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            var boundModelObject = base.BindModel(controllerContext, bindingContext);

            var properties = bindingContext.ModelType.GetProperties().Where(a => a.CanWrite);
            foreach (var propertyInfo in properties)
            {
                object outValue = null;
                bindingContext.TryGetValue(propertyInfo.Name, propertyInfo.DeclaringType, out outValue);
                propertyInfo.SetValue(boundModelObject, outValue, null);
            }

            return boundModelObject;
        }
    }

这是测试脚本。

[TestMethod]
public void TestFooBinding()
{
    var dict = new ValueProviderDictionary(null)
                   {
                       {"Number", new ValueProviderResult("2", "2", null)},
                       {"Test", new ValueProviderResult("12", "12", null)},
                   };

    var bindingContext = new ModelBindingContext() { ModelName = "foo", ValueProvider = dict};

    var target = new MagicBinder();

    Foo result = (Foo)target.BindModel(null, bindingContext);
}

public class Foo
{
    public int Number { get; set; }
    public int Test { get; set; }
}

问题?在 MagicBinder 中,bindingContext.Model 为空。如果我尝试设置它 bindingContext.Model = new Foo().我收到一个异常说它已被弃用,我应该设置 ModelMetadata。

那么我该如何构造一个 ModelMetadata 呢?它甚至不能被嘲笑。

【问题讨论】:

标签: asp.net-mvc-3


【解决方案1】:

注意:此答案适用于 .NET Framework 上的 ASP.NET,可能已过时。

试试这样:

[TestMethod]
public void TestFooBinding()
{
    // arrange
    var formCollection = new NameValueCollection 
    {
        { "Number", "2" },
        { "Test", "12" },
    };

    var valueProvider = new NameValueCollectionValueProvider(formCollection, null);
    var metadata = ModelMetadataProviders.Current.GetMetadataForType(null, typeof(Foo));
    var bindingContext = new ModelBindingContext
    {
        ModelName = "",
        ValueProvider = valueProvider,
        ModelMetadata = metadata
    };
    var controllerContext = new ControllerContext();
    var sut = new MagicBinder();
        
    // act    
    Foo actual = (Foo)sut.BindModel(controllerContext, bindingContext);

    // assert
    // TODO:
}

【讨论】:

  • 非常感谢。使用 ModelMetadata 具体类的原因是什么?为什么他们不能只实现一个接口,因为它所做的就是为模型类型提供“元数据”。
  • 对于遇到此问题的任何人来说,仅供参考:如果您需要访问 metadata.Model,那么您必须设置访问器,它是 GetMetadataForType 的第一个参数,并且必须是 Func所以可以简单到() => new MyModelClass()
  • 在 .NET Framework 中未过时,但在 .NET Core 中无法使用
【解决方案2】:

如果你们中的任何人需要它来为 web-api 工作,您可以使用此方法来测试获取请求,您将受益于使用内置提供程序:

这将填充来自网络的值,而不是获得创建提供者可能永远不会返回 Null 等值的奇怪副作用。

using System;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Metadata.Providers;
using System.Web.Http.ModelBinding;
using System.Web.Http.ValueProviders.Providers;

namespace Apps.API.Web.Tests
{
    public class ModelBinderTestRule
    {
        //This URL is just a place holder for prefixing the query string
        public const string MOCK_URL = "http://localhost:8088/";

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

【讨论】:

  • 次要点:如果您碰巧在活页夹中使用模型验证,这将崩溃。添加“httpControllerContext.Configuration = new HttpConfiguration()”将防止崩溃。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-11
相关资源
最近更新 更多