다음 ModelState.IsValid
과 같이 Employee를 만드는 컨트롤러 작업 메서드를 확인 하고 있습니다.
[HttpPost]
public virtual ActionResult Create(EmployeeForm employeeForm)
{
if (this.ModelState.IsValid)
{
IEmployee employee = this._uiFactoryInstance.Map(employeeForm);
employee.Save();
}
// Etc.
}
Moq Framework를 사용하여 단위 테스트 방법으로 모의하고 싶습니다. 나는 이것을 다음과 같이 조롱하려고했습니다.
var modelState = new Mock<ModelStateDictionary>();
modelState.Setup(m => m.IsValid).Returns(true);
그러나 이것은 내 단위 테스트 케이스에서 예외를 던집니다. 누구든지 여기서 나를 도울 수 있습니까?
답변
조롱 할 필요가 없습니다. 컨트롤러가 이미있는 경우 테스트를 초기화 할 때 모델 상태 오류를 추가 할 수 있습니다.
// arrange
_controllerUnderTest.ModelState.AddModelError("key", "error message");
// act
// Now call the controller action and it will
// enter the (!ModelState.IsValid) condition
var actual = _controllerUnderTest.Index();
답변
위의 솔루션에 대한 유일한 문제는 속성을 설정하면 실제로 모델을 테스트하지 않는다는 것입니다. 이 방법으로 컨트롤러를 설정했습니다.
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;
}
modelBinder 개체는 모델의 유효성을 테스트하는 개체입니다. 이렇게하면 개체의 값을 설정하고 테스트 할 수 있습니다.
답변
uadrive의 대답은 저를 길의 일부로 이끌었지만 여전히 약간의 차이가있었습니다. 에 대한 입력에 데이터가 없으면 new NameValueCollectionValueProvider()
모델 바인더는 컨트롤러를 model
개체가 아닌 빈 모델에 바인딩 합니다.
괜찮습니다. 모델을로 직렬화 NameValueCollection
한 다음 NameValueCollectionValueProvider
생성자에 전달하면됩니다 . 글쎄요. 불행히도 내 모델에는 컬렉션이 포함되어 있고 컬렉션과 잘 작동하지 않기 때문에 내 경우에는 작동 NameValueCollectionValueProvider
하지 않았습니다.
는 JsonValueProviderFactory
하지만, 여기에 구조에 온다. DefaultModelBinder
콘텐츠 유형을 "application/json
“로 지정하고 직렬화 된 JSON 개체를 요청의 입력 스트림에 전달 하는 한 사용할 수 있습니다 (이 입력 스트림은 메모리 스트림이므로 메모리로 처리하지 않아도 괜찮습니다. 스트림은 외부 리소스를 보유하지 않습니다) :
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;
}