如何使用 NUnit 对单个 Fluent 验证规则进行单元测试?
How to unit test a single Fluent Validation Rule wit NUnit?
我有一个流畅的验证器配置,如下所示:
public class Validator : AbstractValidator<Command>
{
public Validator(IDbContext dbContext)
{
RuleFor(c => c.PersonId)
.EntityExists<Command, Person>(dbContext, nameof(Command.PersonId));
RuleFor(c => c.ProjectId)
.Cascade(CascadeMode.Stop)
.EntityExists<Command, Project>(dbContext, nameof(Command.ProjectId))
.MustAsync(async (projectId, cancellation) => !(await dbContext.FindAsync<Project>(new object[] { projectId }, cancellation)).IsCompleted)
.WithMessage("The project is completed. You may not set the participation of a completed project");
RuleFor(c => c.StatusId)
.EntityExists<Command, Status>(dbContext, nameof(Command.StatusId))
...
}
}
我想使用 NUnit、NSubstitute 和 Fluent Validation Extensions 对第二条规则进行单元测试:
[Test]
public void Should_Not_Have_Validation_Error_If_Valid_ProjectId_Is_Supplied()
{
_dbContext.EntityExistsAsync<Project>(Arg.Any<Guid>(), Arg.Any<CancellationToken>()).Returns(true);
_validator.ShouldNotHaveValidationErrorFor(command => command.ProjectId, Guid.NewGuid());
}
由于缺少 PersonId,测试失败。我正在测试 ProjectId 的验证规则,但测试包括 PersonId 的验证规则。如果我模拟 dbContext 使 PersonId 规则通过,测试会因为缺少 StatusId 而失败,这也是一个单独定义的验证规则。
我如何才能测试一个单独的规则 属性 而不必模拟模型中所有其他属性的规则?
validate 方法允许您使用选项并指定要检查的属性。 (默认情况下它包括所有属性的规则)
因此,这里有一个示例(此代码将仅针对 StatudId 验证规则)
_validator.Validate(command, options =>
{
options.IncludeProperties(x => x.StatusId);
});
可以在此处的官方网站上找到更多详细信息。
https://docs.fluentvalidation.net/en/latest/start.html#throwing-exceptions
在@Евгений Елисеев 的提示下(谢谢!)我能够编写仅测试单个规则的测试扩展 属性:
public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
{
var instanceToValidate = new T();
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
memberAccessor.Set(instanceToValidate, value);
TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
return testValidationResult.ShouldHaveValidationErrorFor(expression);
}
public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
{
TValue value = expression.Compile()(objectToTest);
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
return testValidationResult.ShouldHaveValidationErrorFor(expression);
}
public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
{
var instanceToValidate = new T();
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
memberAccessor.Set(instanceToValidate, value);
TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
testValidationResult.ShouldNotHaveValidationErrorFor(expression);
}
public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
{
TValue value = expression.Compile()(objectToTest);
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
testValidationResult.ShouldNotHaveValidationErrorFor(expression);
}
我有一个流畅的验证器配置,如下所示:
public class Validator : AbstractValidator<Command>
{
public Validator(IDbContext dbContext)
{
RuleFor(c => c.PersonId)
.EntityExists<Command, Person>(dbContext, nameof(Command.PersonId));
RuleFor(c => c.ProjectId)
.Cascade(CascadeMode.Stop)
.EntityExists<Command, Project>(dbContext, nameof(Command.ProjectId))
.MustAsync(async (projectId, cancellation) => !(await dbContext.FindAsync<Project>(new object[] { projectId }, cancellation)).IsCompleted)
.WithMessage("The project is completed. You may not set the participation of a completed project");
RuleFor(c => c.StatusId)
.EntityExists<Command, Status>(dbContext, nameof(Command.StatusId))
...
}
}
我想使用 NUnit、NSubstitute 和 Fluent Validation Extensions 对第二条规则进行单元测试:
[Test]
public void Should_Not_Have_Validation_Error_If_Valid_ProjectId_Is_Supplied()
{
_dbContext.EntityExistsAsync<Project>(Arg.Any<Guid>(), Arg.Any<CancellationToken>()).Returns(true);
_validator.ShouldNotHaveValidationErrorFor(command => command.ProjectId, Guid.NewGuid());
}
由于缺少 PersonId,测试失败。我正在测试 ProjectId 的验证规则,但测试包括 PersonId 的验证规则。如果我模拟 dbContext 使 PersonId 规则通过,测试会因为缺少 StatusId 而失败,这也是一个单独定义的验证规则。
我如何才能测试一个单独的规则 属性 而不必模拟模型中所有其他属性的规则?
validate 方法允许您使用选项并指定要检查的属性。 (默认情况下它包括所有属性的规则)
因此,这里有一个示例(此代码将仅针对 StatudId 验证规则)
_validator.Validate(command, options =>
{
options.IncludeProperties(x => x.StatusId);
});
可以在此处的官方网站上找到更多详细信息。 https://docs.fluentvalidation.net/en/latest/start.html#throwing-exceptions
在@Евгений Елисеев 的提示下(谢谢!)我能够编写仅测试单个规则的测试扩展 属性:
public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
{
var instanceToValidate = new T();
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
memberAccessor.Set(instanceToValidate, value);
TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
return testValidationResult.ShouldHaveValidationErrorFor(expression);
}
public static IEnumerable<ValidationFailure> ShouldHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
{
TValue value = expression.Compile()(objectToTest);
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
return testValidationResult.ShouldHaveValidationErrorFor(expression);
}
public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator,
Expression<Func<T, TValue>> expression, TValue value) where T : class, new()
{
var instanceToValidate = new T();
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
memberAccessor.Set(instanceToValidate, value);
TestValidationResult<T> testValidationResult = validator.TestValidate(instanceToValidate, opt => opt.IncludeProperties(memberAccessor.Member.Name));
testValidationResult.ShouldNotHaveValidationErrorFor(expression);
}
public static void ShouldNotHaveValidationErrorForExact<T, TValue>(this IValidator<T> validator, Expression<Func<T, TValue>> expression, T objectToTest) where T : class
{
TValue value = expression.Compile()(objectToTest);
var memberAccessor = new MemberAccessor<T, TValue>(expression, true);
TestValidationResult<T> testValidationResult = validator.TestValidate(objectToTest, opt => opt.IncludeProperties(memberAccessor.Member.Name));
testValidationResult.ShouldNotHaveValidationErrorFor(expression);
}