C# 9.1.3 FluentValidation是否更新了验证程序模拟?

C# 9.1.3 FluentValidation是否更新了验证程序模拟?,c#,fluentvalidation,C#,Fluentvalidation,我更新到包的9.1.3版本,现在我的验证器模拟无法工作 不管.Validate()返回true还是false,代码都会以某种方式运行 以下是验证程序模拟的代码: .Setup(x=>x.Validate(It.IsAny()).IsValid) 。报税表(虚假); Assert.Throws(()=>command.Execute(请求),“位置字段验证错误”); Verify(repository=>repository.EditPosition(It.IsAny()),Times.Neve

我更新到包的9.1.3版本,现在我的验证器模拟无法工作

不管
.Validate()
返回
true
还是
false
,代码都会以某种方式运行

以下是验证程序模拟的代码:


.Setup(x=>x.Validate(It.IsAny()).IsValid)
。报税表(虚假);
Assert.Throws(()=>command.Execute(请求),“位置字段验证错误”);
Verify(repository=>repository.EditPosition(It.IsAny()),Times.Never);
以下是测试失败的原因:

Message: 
      Position field validation error
      Expected: <FluentValidation.ValidationException>
      But was:  null
消息:
位置字段验证错误
预期:
但是是:空
Validator.cs:

公共类SampleValidator:AbstractValidator
{
公共样本验证器()
{
RuleFor(position=>position.Name)
.NotEmpty()
.最大长度(80)
.WithMessage(“职位名称太长”);
规则(位置=>position.Description)
.NotEmpty()
.最大长度(350)
.WithMessage(“职位描述太长”);
}
}
依赖项注入:

services.AddTransient();
用法:

公共类SampleCommand:ISampleCommand
{
专用只读IValidator验证器;
私有只读ISampleRepository存储库;
专用只读IMapper映射器;
公共抽样命令(
[FromServices]IValidator验证器,
[FromServices]是示例存储库,
[FromServices]IMapper映射器)
{
this.validator=验证程序;
this.repository=存储库;
this.mapper=mapper;
}
公共bool执行(职位请求)
{
validator.ValidateAndThrow(请求);
var position=mapper.Map(请求);
返回repository.EditPosition(position);
}
}
测试中的验证程序模拟:

privatemock;
...
validatorMock=newmock();
更新


在更新之前,所有测试都运行良好。现在它们都毁了,我不得不安装上一个版本。

在我的评论中展开:

是的,9.1改变了抛出验证异常的处理方式

一些背景:

验证程序类返回带有
IsValid
布尔属性的
ValidationResult
ValidateAndThrow
扩展方法检查此属性,如果
IsValid
为false,则抛出异常。如果模拟验证程序,如果模拟返回无效的验证结果,仍然可以在模拟上使用“real”
ValidateAndThrow
扩展方法抛出异常

在FluentValidation 9.1中,抛出异常的逻辑从扩展方法中移出,进入到
RaiseValidationException
中的验证器类本身。这样做是为了可以定制引发异常的逻辑(通过重写此方法),而这在以前是扩展方法时是无法做到的

//这是早于9.1的ValidateAndThrow方法定义版本
公共静态void ValidateAndThrow(此IValidator验证器,T实例){
var result=validator.Validate(实例);
如果(!result.IsValid){
抛出新的ValidationException(result.Errors);
}
}
//这是9.1及更新版本中的ValidateAndThrow方法
公共静态void ValidateAndThrow(此IValidator验证器,T实例){
validator.Validate(实例,选项=>{
options.ThrowOnFailures();
});
}
对于运行时使用,这没有什么区别-仍然会引发异常(除非您已重写该方法以防止此情况发生)

然而,这有一个副作用,即如果您依赖于扩展方法而不是验证器引发的异常,这将产生不希望的结果。实际上,只有在模拟验证器时才会出现这种情况。现在,当您创建一个mock时,不会抛出异常,因为mock的行为不像一个真正的验证器

我对FluentValidation的建议一直是“不要模拟验证器”,而是将它们视为黑盒,并为测试目的提供具有有效/无效输入的真实验证器实例-这从长远来看会导致更不脆弱的测试。然而,我也知道,如果你已经有很多测试,那么用这种方式重写测试是不可能的

作为一种解决方法,您可以模拟采用
ValidationContext
Validate
重载,并检查
ThrowOnFailures
属性的上下文,如果设置为true,则让您的模拟引发异常

但是,请注意,如果您这样做,您可能会遇到这样的情况:您的模拟行为是单向的,而真正的验证器的行为是不同的(如果它的RaiseValidationException消息已被覆盖)

由于这是一个突破性的变化,它不应该已经在一个主要版本?理想情况下是的,这是我的错,因为我没有预见到这个特定的用例

编辑:下面是一个创建模拟的示例,用于检查
ThrowOnFailures
属性。该示例使用Moq库,但同样的概念也适用于其他模拟库

private static Mock CreateFailingMockValidator(){
var mockValidator=new Mock();
var failureResult=新的ValidationResult(新列表(){
新验证失败(“Foo”、“Bar”)
});
//设置使用实例的Validate/ValidateAsync重载。
//它们永远不会抛出异常。
mockValidator.Setup(p=>p.Validate(It.IsAny()))
.Returns(failureResult).Verifiable();
mockValidator.Setup(p=>p.ValidateAsync(It.IsAny(),It.IsAny())
.ReturnsAsync(故障结果);
//设置Validate/ValidateAsync重载,该重载将