简体   繁体   English

Moq:模拟实现IValidatableObject?

[英]Moq: Mocking Implementation of IValidatableObject?

Let's say that I have a method that wishes to extract the ValidationResults from a concrete instance of an IValidatableObject and stick them somewhere else...say, from a DTO to a controller's ModelState. 假设我有一个方法希望从IValidatabletableObject的具体实例中提取ValidationResults,并将其粘贴到其他位置...例如,从DTO到控制器的ModelState。

How do I go about mocking the object that implements IValidatableObject? 我该如何模拟实现IValidatableObject的对象?

Say, I had some code like this (doesn't yet compile): 说,我有一些这样的代码(尚未编译):

[Test]
public void GivenBadObjectMethodShouldTransferErrorsToModelstate()
{
  // Mock BadObject and controller
  var controller = new Controller(); 
  var badObject = new Mock<ObjectImplementingIValidatableObject>();
  badObject.Setup( x => x.Validate(It.IsAny<ValidationContext>() )
           .Returns( new List<ValidationResult> { new ValidationResult("error") });

  // Invoke the method
  controller.TransferErrorsToModelStateFrom(badObject);

  // Check that ModelState contains the errors expected
  Assert.IsTrue(controller.ModelState.ContainsKeys("error") );
}

One issue I run into is with ValidationContext, getting errors like below: System.NotSupportedException: Invalid setup on a non-virtual member x => x.Validate(It.IsAny()) 我遇到的一个问题是ValidationContext,出现如下错误:System.NotSupportedException:在非虚拟成员上的无效设置x => x.Validate(It.IsAny())

Can you provide some insight on how to better approach this problem? 您能否提供一些有关如何更好地解决此问题的见解?

Thanks, 谢谢,

Chris 克里斯

You're mocking the ObjectImplementingIValidatableObject concrete type . 您正在嘲笑ObjectImplementingIValidatableObject 具体类型 This is probably not an interface and its Validate method is declared non-virtual . 这可能不是接口,并且其Validate方法被声明为non-virtual This is a situation most isolation frameworks like Moq cannot handle , as they work by dynamically generating subclasses (or implementations) of the mocked types at runtime. 这是大多数隔离框架(如Moq)无法处理的情况 ,因为它们通过在运行时动态生成模拟类型的子类(或实现)来工作。

If you provide an interface to be mocked, the isolation framework generates a fake implementation at runtime. 如果提供要模拟的接口,则隔离框架会在运行时生成伪造的实现。 If you provide a concrete type, the methods you stub/mock must be virtual in order for the framework to override them in the dynamically created subclass. 如果提供具体类型,则存根/模拟的方法必须是虚拟的,以便框架在动态创建的子类中覆盖它们。

So in order for your snippet to work, consider working against an interface (perhaps just IValidatableObject) or making the Validate method declared on ObjectImplementingIValidatableObject virtual. 因此,为了使您的代码片段正常工作,请考虑使用接口(也许只是IValidatableObject)或将在ObjectImplementingIValidatableObject上声明的Validate方法设为虚拟。

Easy to lose sight of programming to interfaces when swimming in all these things. 在进行所有这些操作时,很容易忽略对接口编程的视线。 Thanks for your help! 谢谢你的帮助!

This is the code I ended up with: (I've included everything, though there are parts I could pull out into a setup method) 这是我最终得到的代码:(我已经包含了所有内容,尽管有些部分可以引入设置方法中)

[Test]
    public void GivenValidatableObjectWithMemberErrorThenMethodShouldTransferMemberErrorToModelstate()
    {
        // Setup, getting protected member of abstract class instance
        var controller = (TestConcreteController)FormatterServices
            .GetUninitializedObject(typeof(TestConcreteController));

        MethodInfo ApplyErrorsToModelStateFromModel = controller
            .GetType()
            .GetMethod("ApplyErrorsToModelStateFromModel",
            BindingFlags.Instance | BindingFlags.NonPublic);

        // Setup, building expected objects to pass into method
        IEnumerable<ValidationResult> errors = new List<ValidationResult> 
        { 
            new ValidationResult(TestConstant.ExpectedErrorMessage, TestConstant.ExpectedMembers)
        };

        var objectWithMemberError = new Mock<IValidatableObject>();
        objectWithMemberError.Setup(x => x.Validate(It.IsAny<ValidationContext>()))
                             .Returns(errors);

        // Call the method
        ApplyErrorsToModelStateFromModel.Invoke(controller, new[] { objectWithMemberError.Object });

        // Check that member was added to list and contains the expected error
        Assert.IsTrue(controller.ModelState[TestConstant.ExpectedMembers[0]].Errors[0].ErrorMessage == TestConstant.ExpectedErrorMessage);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM