Estoy comprobando el ModelState.IsValid
método de acción de mi controlador que crea un empleado como este:
[HttpPost]
public virtual ActionResult Create(EmployeeForm employeeForm)
{
if (this.ModelState.IsValid)
{
IEmployee employee = this._uiFactoryInstance.Map(employeeForm);
employee.Save();
}
// Etc.
}
Quiero simularlo en mi método de prueba unitaria usando Moq Framework. Traté de burlarme de esto así:
var modelState = new Mock<ModelStateDictionary>();
modelState.Setup(m => m.IsValid).Returns(true);
Pero esto arroja una excepción en mi caso de prueba unitaria. ¿Puede alguien ayudarme aquí?
c#
asp.net-mvc
unit-testing
mocking
moq
Mazen
fuente
fuente
El único problema que tengo con la solución anterior es que en realidad no prueba el modelo si configuro atributos. Configuré mi controlador de esta manera.
private HomeController GenerateController(object model) { HomeController controller = new HomeController() { RoleService = new MockRoleService(), MembershipService = new MockMembershipService() }; MvcMockHelpers.SetFakeAuthenticatedControllerContext(controller); // bind errors modelstate to the controller var modelBinder = new ModelBindingContext() { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, model.GetType()), ValueProvider = new NameValueCollectionValueProvider(new NameValueCollection(), CultureInfo.InvariantCulture) }; var binder = new DefaultModelBinder().BindModel(new ControllerContext(), modelBinder); controller.ModelState.Clear(); controller.ModelState.Merge(modelBinder.ModelState); return controller; }
El objeto modelBinder es el objeto que prueba la validez del modelo. De esta manera, puedo establecer los valores del objeto y probarlo.
fuente
La respuesta de uadrive me llevó parte del camino, pero aún quedaban algunas lagunas. Sin ningún dato en la entrada
new NameValueCollectionValueProvider()
, la carpeta de modelos vinculará el controlador a un modelo vacío, no almodel
objeto.Está bien, simplemente serialice su modelo como
NameValueCollection
y luego páselo alNameValueCollectionValueProvider
constructor. Bueno, no del todo. Desafortunadamente, no funcionó en mi caso porque mi modelo contiene una colección yNameValueCollectionValueProvider
no funciona bien con las colecciones.Sin
JsonValueProviderFactory
embargo, viene al rescate aquí. Puede ser utilizado por elDefaultModelBinder
siempre que especifique un tipo de contenido de"application/json
"y pase su objeto JSON serializado al flujo de entrada de su solicitud (tenga en cuenta que, debido a que este flujo de entrada es un flujo de memoria, está bien dejarlo sin eliminar, como una memoria stream no se aferra a ningún recurso externo):protected void BindModel<TModel>(Controller controller, TModel viewModel) { var controllerContext = SetUpControllerContext(controller, viewModel); var bindingContext = new ModelBindingContext { ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => viewModel, typeof(TModel)), ValueProvider = new JsonValueProviderFactory().GetValueProvider(controllerContext) }; new DefaultModelBinder().BindModel(controller.ControllerContext, bindingContext); controller.ModelState.Clear(); controller.ModelState.Merge(bindingContext.ModelState); } private static ControllerContext SetUpControllerContext<TModel>(Controller controller, TModel viewModel) { var controllerContext = A.Fake<ControllerContext>(); controller.ControllerContext = controllerContext; var json = new JavaScriptSerializer().Serialize(viewModel); A.CallTo(() => controllerContext.Controller).Returns(controller); A.CallTo(() => controllerContext.HttpContext.Request.InputStream).Returns(new MemoryStream(Encoding.UTF8.GetBytes(json))); A.CallTo(() => controllerContext.HttpContext.Request.ContentType).Returns("application/json"); return controllerContext; }
fuente