如何模拟使用规则集的 AbstractValidator?
How do I mock an AbstractValidator that is using rule sets?
我有一个抽象验证器,具有以下结构:
public abstract class RiskAssessmentServiceCreateRequestValidator<T>
: AbstractValidator<T> where T
: IRiskAssessmentServiceCreateRequest
{
public RiskAssessmentServiceCreateRequestValidator(ApplicationContext context)
{
RuleSet("modelBinding", () =>
{
RuleFor(x => x.ServiceProviderId).NotNull().GreaterThan(0);
});
RuleSet("handler", () =>
{
//....
});
}
}
在我的请求处理程序中,我正在调用此 class 的派生实例,如下所示:
var validationResult = _validator.Validate(request, ruleSet: "handler");
如何在单元测试中模拟对 Validate 的特定调用?如果我不使用规则集,我的设置将如下所示:
_validator.Setup(x => x.Validate(It.IsAny<CreateRequest>()))
.Returns(validationResult);
不允许以下调用,因为表达式树中不允许使用可选参数:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
ruleSet: It.IsAny<string>()))
.Returns(validationResult);
理论上我可以这样设置:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
(IValidatorSelector)null,
It.IsAny<string>()))
.Returns(validationResult);
但这会导致:
System.NotSupportedException : Unsupported expression: x => x.Validate<CreateRequest>(It.IsAny<CreateRequest>(), null, It.IsAny<string>())
Extension methods (here: DefaultValidatorExtensions.Validate) may not be used in setup / verification expressions.
除了我想避免使用真正的验证器之外,我该如何解决这个问题并以合适的方式设置起订量?
尝试
var mock = new Mock<AbstractValidator<object>>();
mock.Setup(x => x.Validate(It.Is<ValidationContext<object>>(ctx => IsExpectedRuleSet(ctx, new[] { "Rule1", "Rule2" }))))
.Return(...);
mock.Object.Validate(new object(), ruleSet: "Rule1,Rule2");
bool IsExpectedRuleSet(ValidationContext context, string[] expectedRuleSet)
{
return (context.Selector as FluentValidation.Internal.RulesetValidatorSelector)?.RuleSets.SequenceEqual(expectedRuleSet) == true;
}
这里确实有两个问题。
首先是如何使用可选参数进行mock - 简单地将可选参数视为非可选参数。
但是,您正在尝试模拟扩展方法,这是不可能的。相反,您需要模拟扩展试图调用的方法。粗略地浏览一下源代码,我认为它在幕后调用 validator.Validate(ValidationContext)
所以你的起订量代码可能是这样的:
_validator
.Setup(x => x.Validate(It.IsAny<ValidationContext<CreateRequest>>())
.Returns(validationResult);
我有一个抽象验证器,具有以下结构:
public abstract class RiskAssessmentServiceCreateRequestValidator<T>
: AbstractValidator<T> where T
: IRiskAssessmentServiceCreateRequest
{
public RiskAssessmentServiceCreateRequestValidator(ApplicationContext context)
{
RuleSet("modelBinding", () =>
{
RuleFor(x => x.ServiceProviderId).NotNull().GreaterThan(0);
});
RuleSet("handler", () =>
{
//....
});
}
}
在我的请求处理程序中,我正在调用此 class 的派生实例,如下所示:
var validationResult = _validator.Validate(request, ruleSet: "handler");
如何在单元测试中模拟对 Validate 的特定调用?如果我不使用规则集,我的设置将如下所示:
_validator.Setup(x => x.Validate(It.IsAny<CreateRequest>()))
.Returns(validationResult);
不允许以下调用,因为表达式树中不允许使用可选参数:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
ruleSet: It.IsAny<string>()))
.Returns(validationResult);
理论上我可以这样设置:
_validator.Setup(x => x.Validate(
It.IsAny<CreateRequest>(),
(IValidatorSelector)null,
It.IsAny<string>()))
.Returns(validationResult);
但这会导致:
System.NotSupportedException : Unsupported expression: x => x.Validate<CreateRequest>(It.IsAny<CreateRequest>(), null, It.IsAny<string>())
Extension methods (here: DefaultValidatorExtensions.Validate) may not be used in setup / verification expressions.
除了我想避免使用真正的验证器之外,我该如何解决这个问题并以合适的方式设置起订量?
尝试
var mock = new Mock<AbstractValidator<object>>();
mock.Setup(x => x.Validate(It.Is<ValidationContext<object>>(ctx => IsExpectedRuleSet(ctx, new[] { "Rule1", "Rule2" }))))
.Return(...);
mock.Object.Validate(new object(), ruleSet: "Rule1,Rule2");
bool IsExpectedRuleSet(ValidationContext context, string[] expectedRuleSet)
{
return (context.Selector as FluentValidation.Internal.RulesetValidatorSelector)?.RuleSets.SequenceEqual(expectedRuleSet) == true;
}
这里确实有两个问题。
首先是如何使用可选参数进行mock - 简单地将可选参数视为非可选参数。
但是,您正在尝试模拟扩展方法,这是不可能的。相反,您需要模拟扩展试图调用的方法。粗略地浏览一下源代码,我认为它在幕后调用
validator.Validate(ValidationContext)
所以你的起订量代码可能是这样的:_validator .Setup(x => x.Validate(It.IsAny<ValidationContext<CreateRequest>>()) .Returns(validationResult);