Sprawdzam ModelState.IsValid
metodę akcji kontrolera, która tworzy pracownika w następujący sposób:
[HttpPost]
public virtual ActionResult Create(EmployeeForm employeeForm)
{
if (this.ModelState.IsValid)
{
IEmployee employee = this._uiFactoryInstance.Map(employeeForm);
employee.Save();
}
// Etc.
}
Chcę go wyśmiewać w mojej metodzie testów jednostkowych przy użyciu Moq Framework. Próbowałem to kpić w ten sposób:
var modelState = new Mock<ModelStateDictionary>();
modelState.Setup(m => m.IsValid).Returns(true);
Ale to zgłasza wyjątek w moim przypadku testu jednostkowego. Czy ktoś może mi tu pomóc?
c#
asp.net-mvc
unit-testing
mocking
moq
Mazen
źródło
źródło
Jedyny problem z powyższym rozwiązaniem polega na tym, że w rzeczywistości nie testuje ono modelu, jeśli ustawię atrybuty. Skonfigurowałem kontroler w ten sposób.
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; }
Obiekt modelBinder jest obiektem testującym poprawność modelu. W ten sposób mogę po prostu ustawić wartości obiektu i przetestować go.
źródło
Odpowiedź uadrive zajęła mi część drogi, ale nadal były pewne luki. Bez żadnych danych wejściowych do
new NameValueCollectionValueProvider()
, spinacz modelu powiąże kontroler z pustym modelem, a nie zmodel
obiektem.W porządku - po prostu serializuj swój model jako a
NameValueCollection
, a następnie przekaż go doNameValueCollectionValueProvider
konstruktora. Cóż, niezupełnie. Niestety w moim przypadku to nie zadziałało, ponieważ mój model zawiera kolekcję iNameValueCollectionValueProvider
nie bawi się ładnie z kolekcjami.Tutaj jednak
JsonValueProviderFactory
przychodzi z pomocą. Może być używany przez, oDefaultModelBinder
ile określisz typ zawartości"application/json
„i przekażesz zserializowany obiekt JSON do strumienia wejściowego żądania (Uwaga, ponieważ ten strumień wejściowy jest strumieniem pamięci, można go pozostawić niewykorzystany jako pamięć stream nie zatrzymuje żadnych zasobów zewnętrznych):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; }
źródło