Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/256.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用Moq框架模拟ModelState.IsValid?_C#_Asp.net Mvc_Unit Testing_Mocking_Moq - Fatal编程技术网

C# 如何使用Moq框架模拟ModelState.IsValid?

C# 如何使用Moq框架模拟ModelState.IsValid?,c#,asp.net-mvc,unit-testing,mocking,moq,C#,Asp.net Mvc,Unit Testing,Mocking,Moq,我正在检查我的控制器操作方法中的ModelState.IsValid,该方法创建如下员工: [HttpPost] public virtual ActionResult Create(EmployeeForm employeeForm) { if (this.ModelState.IsValid) { IEmployee employee = this._uiFactoryInstance.Map(employeeForm); employee.S

我正在检查我的控制器操作方法中的
ModelState.IsValid
,该方法创建如下员工:

[HttpPost]
public virtual ActionResult Create(EmployeeForm employeeForm)
{
    if (this.ModelState.IsValid)
    {
        IEmployee employee = this._uiFactoryInstance.Map(employeeForm);
        employee.Save();
    }

    // Etc.
}
我想在使用Moq框架的单元测试方法中模拟它。我试着这样嘲笑它:

var modelState = new Mock<ModelStateDictionary>();
modelState.Setup(m => m.IsValid).Returns(true);
var modelState=new Mock();
Setup(m=>m.IsValid).返回(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的回答让我了解了一部分,但仍然存在一些差距。如果
新名称ValueCollectionValueProvider()
的输入中没有任何数据,则模型绑定器将控制器绑定到一个空模型,而不是
模型
对象

这很好——只需将模型序列化为
NameValueCollection
,然后将其传递给
NameValueCollectionValueProvider
构造函数。嗯,不完全是。不幸的是,它在我的例子中不起作用,因为我的模型包含一个集合,
NameValueCollectionValueProvider
不能很好地处理集合

然而,
JsonValueProviderFactory
在这里起到了解救作用。只要您指定内容类型为
“application/json
”,并将序列化的json对象传递到请求的输入流中(请注意,因为此输入流是内存流,所以可以将其保持不变,因为内存流不会保留任何外部资源),那么
DefaultModelBinder
就可以使用它:

受保护的void BindModel(控制器控制器,TModel viewModel)
{
var controllerContext=SetUpControllerContext(控制器,视图模型);
var bindingContext=新模型bindingContext
{
ModelMetadata=ModelMetadataProviders.Current.GetMetadataForType(()=>viewModel,typeof(TModel)),
ValueProvider=new JsonValueProviderFactory().GetValueProvider(controllerContext)
};
新的DefaultModelBinder().BindModel(controller.ControllerContext,bindingContext);
controller.ModelState.Clear();
controller.ModelState.Merge(bindingContext.ModelState);
}
专用静态控制器上下文设置控制器上下文(控制器控制器,TModel viewModel)
{
var controllerContext=A.Fake();
controller.ControllerContext=ControllerContext;
var json=new JavaScriptSerializer().Serialize(viewModel);
A.CallTo(()=>controllerContext.Controller).返回(Controller);
A.CallTo(()=>controllerContext.HttpContext.Request.InputStream).Returns(newMemoryStream(Encoding.UTF8.GetBytes(json));
A.CallTo(()=>controllerContext.HttpContext.Request.ContentType).Returns(“application/json”);
返回控制器上下文;
}

非常好,这正是我想要的。我不知道有多少人在这样一个老问题上发帖,但它对我来说很有价值。谢谢。似乎是一个很好的解决方案,还在2016年:)用这样的东西单独测试模型不是更好吗?虽然这是一个聪明的解决方案,但我同意@RubberDuck。对于实际的独立单元测试,模型的验证应该是它自己的测试,而控制器的测试应该有它自己的测试。如果模型更改为违反ModelBinder验证,则控制器测试将失败,这是一个误报,因为控制器逻辑没有中断。要测试无效的ModelStateDictionary,只需为ModelState.IsValid检查添加一个虚假的ModelState错误以失败。我们如何设置ModelState.IsValid以达到真实情况?ModelState没有setter,因此我们无法执行以下操作:\ u controllerUnderTest.ModelState.IsValid=true。没有这一点,它就不会击中目标employee@Newton,默认情况下是正确的。您不需要指定任何内容来符合真实情况。如果你想找到错误的情况,你只需添加一个modelstate错误,如我的答案所示。通过这种方式,您可以获得更真实的控制器行为,您应该将模型验证交付给它的destiny属性验证。下面的帖子描述了这个()
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;
}