2017-10-05 68 views
0

我正在使用偉大的FluentValidation庫,並在單元測試驗證程序中遇到問題。我有以下的例子此Wiki網頁上儘可能接近(但我使用的xUnit)https://github.com/JeremySkinner/FluentValidation/wiki/g.-TestingFluent驗證測試未處理預期空值

它使用ShouldHaveValidationErrorFor擴展方法測試。

我得到一個NullReferenceException與測試失敗。但那正是我所測試的 - 一個必填字段的空引用。

這裏是我的代碼:

驗證:

public class ChangeEmailRequestInputModelValidator : AbstractValidator<ChangeEmailRequestInputModel> 
{ 
    public ChangeEmailRequestInputModelValidator() 
    { 
     RuleFor(x => x.NewEmail) 
      .NotEmpty(); 

     RuleFor(m => m.NewEmail.Trim()) 
      .EmailAddress() 
      .When(m => m.NewEmail != null) 
      .WithMessage(ValidationConstants.SymbolIsNotAValidEmailAddress, x => x.NewEmail) 
      .WithName("NewEmail"); 

     RuleFor(m => m.NewEmailConfirm.Trim()) 
      .Cascade(CascadeMode.Continue) 
      .NotEmpty().WithMessage("Confirm New Email field cannot be empty.") 
      .Equal(m => m.NewEmail.Trim()).WithMessage("Confirm New Email field must be equal to the New Email."); 
    } 
} 

測試:

public class ChangeEmailRequestInputModelValidatorTests 
{ 
    [Fact] 
    public void Errors_Where_NewEmail_Is_Null() 
    { 
     var val = new ChangeEmailRequestInputModelValidator(); 

     val.ShouldHaveValidationErrorFor(v => v.NewEmail, null as string);    
    } 
} 

任何想法,爲什麼發生這種情況?我一定有什麼不對,但看起來和我很模糊的例子很相似。

回答

1

你有三個規則設置:

  1. NEWEMAIL不能爲空
  2. 如果NEWEMAIL不爲空,修剪,並確保它是一個有效的電子郵件地址
  3. 修剪NewEmailConfirm,確保它不是空,將其與修剪過的NewEmail進行比較並確保其匹配。

這是導致問題的第三條規則。 FluentValidation在底層工作的方式意味着,要評價的第一個表達式是傳遞到RuleFor的表達,除非使用WhenUnless,這兩者通過所述規則回溯和應用謂詞或反向謂詞傳遞到那些條件方法。

所以本質上,你的驗證器啓動,它通過3條規則中的2條,然後開始評估第三條規則。您的測試夾具沒有爲NewEmailConfirm設置一個值,因此它開始評估表達式鏈並碰到第一個表達式,即m => m.NewEmailConfirm.Trim(),然後這會爆炸。

你能做些什麼來防止這種情況如下:

[Fact] 
public void Errors_Where_NewEmail_Is_Null() 
{ 
    var sut = new ChangeEmailRequestInputModelValidator(); 
    sut.ShouldHaveValidationErrorFor(v => v.NewEmail, new ChangeEmailRequestInputModel { NewEmail = null, NewEmailConfirm = "[email protected]" }); 
} 

這將實例類,下驗證與正確的位上,首先表達不炸燬。你現在要面對的問題是,在某些時候你打到.Equal(m => m.NewEmail.Trim())。您的燈具明確將此設置爲null以測試規則2,因此規則3仍需要重新構造。

我建議如下:

public ChangeEmailRequestInputModelValidator(){ 

    RuleFor(m => m.NewEmail) 
     .Cascade(CascadeMode.StopOnFirstFailure) 
     .NotEmpty() 
     .WithMessage("Email is a required field.") 
     .EmailAddress() 
     .WithMessage(
      ValidationConstants.SymbolIsNotAValidEmailAddress, x => x.NewEmail) 
     .WithName("NewEmail"); 

    RuleFor(m => m.NewEmailConfirm) 
     .NotEmpty() 
     .WithMessage("Confirm New Email field cannot be empty."); 

    RuleFor(m => m) 
     .Must(HaveMatchingEmailAndConfirmEmail) 
     .WithMessage("Confirm New Email field must be equal to the New Email."); 
} 

private bool HaveMatchingEmailAndConfirmEmail(ChangeEmailRequestInputModel model) 
{ 
    return model.NewEmail?.Trim() == model.NewEmailConfirm?.Trim(); 
} 

以上仍設法同時驗證您的性質,相互獨立的。然後它只是相互檢查兩個屬性,利用空合併運算符來避開顯式的空檢查。

+0

謝謝。這是完全合理的。我知道問題出在我的代碼上。只是看不到它。 – onefootswill

+0

對不起,重新審視這一點,但不應該當條件保護代碼當值爲null時炸掉。我預計Trim將不會被稱爲「何時」會阻止代碼的執行。 – onefootswill

+0

你是對的,你能不接受我的回答,我會用*真實的*它更新它的原因嗎? –