Reputation: 21261
Pretty new to MVC so hopefully this is a simple question.
I have written a custom binding attribute that requires access to the httpContext
. In order to inject a mock httpContext
during unit tests, I have written an InjectingMetadataProvider
that populates the Context
property on any of my custom attributes.
I have managed to get this to work in the following test:
[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);
}
However, if I comment out the line that sets my InjectingMetadataProvider
to ModelMetadataProviders.Current
, then my InjectingMetadataProvider.CreateMetadata()
override gets handed a blank list of attributes, and so the test fails because my custom attributes don't get their context set.
Why do I need to set it to Current
when I'm using it explicitly anyway? I don't want to be setting static stuff in my tests.
I may be doing something stupid because I'm feeling in the dark a bit at the moment due to my unfamiliarity with the framework.
Upvotes: 1
Views: 1590
Reputation: 835
Inside the DefaultModelBinder, a new binding context is created when calling BindComplexElementalModel. Notice that it gets the metadata from the ModelMetadataProviders.Current, and not your custom model metadata provider.
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;
}
Upvotes: 1