Использование пользовательского ModelMetadataProvider в модульном тесте

Довольно новичок в MVC, так что, надеюсь, это простой вопрос.

Я написал собственный атрибут привязки, который требует доступа к httpContext, Для того, чтобы ввести макет httpContext во время юнит-тестов я написал InjectingMetadataProvider что населяет Context свойство на любом из моих пользовательских атрибутов.

Мне удалось заставить это работать в следующем тесте:

[TestMethod]
public void Marker_ShouldBind_Id()
{
    // Arrange
    var formCollection = new NameValueCollection 
    { 
        { "id", "2" }
    };

    var context = new Mock<HttpContextBase>();
    context.Setup(c => c.User).Returns((IPrincipal)null); 

    var metaProvider = new InjectingMetadataProvider(context.Object);
    ModelMetadataProviders.Current = metaProvider;  //why do I need this?

    var bindingContext = new ModelBindingContext
    {
        ModelName     = string.Empty,
        ValueProvider = new NameValueCollectionValueProvider(formCollection, null),
        ModelMetadata = metaProvider.GetMetadataForType(null, typeof(Marker)),
    };

    var binder = new DefaultModelBinder();

    // Act
    var marker = (Marker)binder.BindModel(new ControllerContext(), bindingContext);

    // Assert
    marker.Id.Should().Be(2);
}

Однако, если я закомментирую строку, которая устанавливает мой InjectingMetadataProvider в ModelMetadataProviders.Currentтогда мой InjectingMetadataProvider.CreateMetadata() Переопределение получает пустой список атрибутов, и поэтому тест не пройден, потому что мои пользовательские атрибуты не получают свой контекстный набор.

Почему мне нужно установить его Current когда я использую это явно в любом случае? Я не хочу устанавливать статические вещи в своих тестах.

Возможно, я делаю что-то глупое, потому что в данный момент чувствую себя немного не в своей тарелке из-за того, что не знаком с фреймворком.

1 ответ

Решение

Внутри DefaultModelBinder новый контекст привязки создается при вызове BindComplexElementalModel. Обратите внимание, что он получает метаданные из ModelMetadataProviders.Current, а не от вашего поставщика метаданных пользовательской модели.

  internal ModelBindingContext CreateComplexElementalModelBindingContext(ControllerContext controllerContext, ModelBindingContext bindingContext, object model) {
        BindAttribute bindAttr = (BindAttribute)GetTypeDescriptor(controllerContext, bindingContext).GetAttributes()[typeof(BindAttribute)];
        Predicate<string> newPropertyFilter = (bindAttr != null)
            ? propertyName => bindAttr.IsPropertyAllowed(propertyName) && bindingContext.PropertyFilter(propertyName)
            : bindingContext.PropertyFilter;

        ModelBindingContext newBindingContext = new ModelBindingContext() {
            ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, bindingContext.ModelType),
            ModelName = bindingContext.ModelName,
            ModelState = bindingContext.ModelState,
            PropertyFilter = newPropertyFilter,
            ValueProvider = bindingContext.ValueProvider
        };

        return newBindingContext;
    }
Другие вопросы по тегам