假设我有一个方法希望从IValidatableObject的具体实例中提取ValidationResults并将它们粘贴在其他地方......例如,从DTO到控制器的ModelState。
如何模拟实现IValidatableObject的对象?
说,我有一些这样的代码(尚未编译):
[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") );
}
我遇到的一个问题是ValidationContext,收到如下错误: System.NotSupportedException:非虚拟成员上的设置无效x =&gt; x.Validate(It.IsAny())
您能否提供一些有关如何更好地解决此问题的见解?
谢谢,
克里斯
答案 0 :(得分:1)
您正在嘲笑ObjectImplementingIValidatableObject 具体类型。这可能不是一个接口,其Validate方法被声明为非虚拟。这是大多数隔离框架(如Moq无法处理)的情况,因为它们通过在运行时动态生成模拟类型的子类(或实现)来工作。
如果提供要模拟的接口,隔离框架会在运行时生成虚假实现。如果提供具体类型,则stub / mock的方法必须是虚拟的,以便框架在动态创建的子类中覆盖它们。
因此,为了使您的代码段能够正常工作,请考虑使用接口(可能只是IValidatableObject)或在ObjectImplementingIValidatableObject上声明Validate方法虚拟。
答案 1 :(得分:0)
在所有这些事情中游泳时,很容易忽视对界面的编程。谢谢你的帮助!
这是我最终得到的代码:(我已经包含了所有内容,但有些部分我可以提取到设置方法中)
[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);
}