简体   繁体   中英

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.

How do I go about mocking the object that implements 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())

Can you provide some insight on how to better approach this problem?

Thanks,

Chris

You're mocking the ObjectImplementingIValidatableObject concrete type . This is probably not an interface and its Validate method is declared 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.

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.

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);
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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