如何对自定义验证属性进行单元测试

61

我有一个自定义的asp.net mvc类验证属性。 我的问题是如何对其进行单元测试? 测试该类是否具有该属性一件事,但这并不能真正测试其中的逻辑。这就是我想要测试的内容。

[Serializable]
[EligabilityStudentDebtsAttribute(ErrorMessage = "You must answer yes or no to all questions")]
public class Eligability
{
    [BooleanRequiredToBeTrue(ErrorMessage = "You must agree to the statements listed")]
    public bool StatementAgree { get; set; }

    [Required(ErrorMessage = "Please choose an option")]
    public bool? Income { get; set; }

.....removed for brevity }

[AttributeUsage(AttributeTargets.Class)]
public class EligabilityStudentDebtsAttribute : ValidationAttribute
{
    // If AnyDebts is true then 
    // StudentDebts must be true or false

    public override bool IsValid(object value)
    {
        Eligability elig = (Eligability)value;
        bool ok = true;
        if (elig.AnyDebts == true)
        {
            if (elig.StudentDebts == null)
            {
                ok = false;
            }
        }
        return ok;

    }
}

我尝试写了以下的测试,但是它并没有起作用:

[TestMethod]
public void Eligability_model_StudentDebts_is_required_if_AnyDebts_is_true()
{

   // Arrange
   var eligability = new Eligability();
   var controller = new ApplicationController();

   // Act
   controller.ModelState.Clear();
   controller.ValidateModel(eligability);
   var actionResult = controller.Section2(eligability,null,string.Empty);

   // Assert
   Assert.IsInstanceOfType(actionResult, typeof(ViewResult));
   Assert.AreEqual(string.Empty, ((ViewResult)actionResult).ViewName);
   Assert.AreEqual(eligability, ((ViewResult)actionResult).ViewData.Model);
   Assert.IsFalse(((ViewResult)actionResult).ViewData.ModelState.IsValid);
}

ModelStateDictionary不包含此自定义属性的键。它仅包含标准验证属性的属性。

为什么会这样呢?

测试这些自定义属性的最佳方法是什么?

3个回答

71

您的属性EligabilityStudentDebtsAttribute只是一个标准类,和其他一样,只需对IsValid()方法进行单元测试。如果它正常工作,请相信框架该属性也能正常工作。

因此:

[Test]
public void AttibuteTest()
{
   // arrange
   var value = //.. value to test - new Eligability() ;
   var attrib = new EligabilityStudentDebtsAttribute();

   // act
   var result = attrib.IsValid(value);

   // assert
   Assert.That(result, Is.True)
}

2
这是执行跨属性验证的最佳方式吗?(涉及超过一个属性的模型验证) - MightyAtom
尝试简单一些,如果对你有效的话就没有理由进行更复杂的测试 :) - Alexander Beletsky
如果我的属性需要像其他属性一样的状态怎么办? - Jordan
2
请记得在您的单元测试程序集中添加对 System.ComponentModel.DataAnnotations 的引用,否则您将看不到 IsValid 方法(它也无法编译,但我花了一段时间才发现这个问题)。 - Liam
有一种方法可以直接测试简单类型,而无需具有属性的对象。我在下面提供了一个答案。它还允许直接从属性中获取消息。 - Ilya Chernomordik

40

你自定义的验证属性可能依赖于其他属性的状态。在这种情况下,你可以使用System.ComponentModel.DataAnnotations.Validator静态方法,例如:

您自定义的验证属性可能取决于其他属性的状态。在这种情况下,您可以使用System.ComponentModel.DataAnnotations.Validator静态方法,例如:

var model = ...
var context = new ValidationContext(model);
var results = new List<ValidationResult>();
var isValid = Validator.TryValidateObject(model, context, results, true);
Assert.True(isValid);

5
你可能想要在验证所有属性时添加 true 标志 - "Validator.TryValidateObject(model, context, results, true);" - 在使用 NUnit 进行验证时,除非将 "validateAllProperties" 设置为 true,否则在 "protected override ValidationResult IsValid(..)" 内的验证不会被触发 - 因此测试未按预期运行,我无法调试代码。 - JimiSweden

13

我发现IsValid在像字符串这样的简单类型上不起作用。例如,如果您对一个不是对象的字符串查询参数进行验证。此外,直接在属性上测试值而无需提供整个对象更容易进行测试。它还允许检查错误消息。它是这样工作的:

string input = "myteststring";
var myAttribute = new MyAttribute()
var result = attribute.GetValidationResult(input, new ValidationContext(input));

var isSuccess = result == ValidationResult.Success;
var errorMessage = result?.ErrorMessage;

这段代码仅测试您输入值的验证,不涉及其他方面。

P.S. 我已在dotnet core上进行了测试,但我认为这对普通的dotnet也适用。


从我的角度来看,这是最好的答案。这使得一个人可以在隔离环境中测试他们的验证类型。 - dblood

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接