2017-05-30 38 views
1

我爲我使用的界面創建了一個自定義Hamcrest匹配器。在自定義Hamcrest Matcher中緩存變量

的匹配是TypeSafeMatcher一個實例,它覆蓋了以下三種方法:

  • TypeSafeMatcher#matchesSafely(T item)boolean
  • TypeSafeMatcher#describeMismatchSafely(T item, Description mismatchDescription)void
  • TypeSafeMatcher#describeTo(Description description)void

I類」 m匹配處理特定類型o的驗證f對象。它來自外部庫,所以我不能簡單地改變它。我們稱這個類爲ValidationSubject

ValidationSubject的每個實例此類定義了要執行的驗證背後的一些邏輯。這是通過實施ValidationSubject#validate(ValidationData validationData)其中validationData是一個建設者型對象,它允許程序員基於一個類的對象的實現ValidationSubject

public class Foo implements ValidationSubject { 

    private String state; 

    private Map<String, Baz> moreState; 

    // constructor, methods affecting the state 

    // this method is required by ValidationSubject 
    @Override 
    public void validate(ValidationData validationData) { 
     /* 
     * call methods on validationData based on the state 
     * of the object 
     */ 
    } 
} 

狀態報告驗證錯誤我用我的匹配測試完成驗證邏輯在每個具體類中實現,例如Foo

爲了做到這一點,我需要在每個測試用例中對ValidationData的實例進行存根/模擬/窺探,並根據被測主題執行的邏輯瞭解對象的狀態是如何改變的。這是很多樣板。我希望我的匹配抽象那遠

assertThat(testedValidationSubject, hasValidationErrors("Illegal character in name", "Description exceeds 200 words", "Age cannot be negative")); 

在這種情況下,我真正匹配對hasValidationErrors匹配的參數是一組被測對象存儲在ValidationData對象的字符串值。

提取這些值需要一些代碼。

return new TypeSafeMatcher<ValidationSubject>() { 

    @Override 
    protected boolean matchesSafely(ValidationSubject item) { 
     // this calls the relevant methods on 'item' internally 
     Validator validator = new Validator(item); 
     List<ValidationMessage> errorMessages = validator.getErrorMessageGroup() 
       .getMessages(); 
     Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage()) 
       .collect(Collectors.toSet()); 
     Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet()); 
     Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages); 
     Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages); 
     return SetUtils.union(unexpectedMessages, missingMessages).isEmpty(); 
    } 

    @Override 
    public void describeMismatchSafely(final ValidationSubject item, final Description description) { 
         // this calls the relevant methods on 'item' internally 
     Validator validator = new Validator(item); 
     List<ValidationMessage> errorMessages = validator.getErrorMessageGroup() 
       .getMessages(); 
     Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage()) 
       .collect(Collectors.toSet()); 
     Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet()); 
     Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages); 
     Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages); 
     description.appendText("Validation errors were missing or unexpected\n") 
       .appendValueList("\tSupefluous messages: ", ", ", "\n", unexpectedMessages.toArray()) 
       .appendValueList("\tMissing messages: ", ", ", "\n", missingMessages.toArray()); 
    } 

    @Override 
    public void describeTo(Description description) { 
     description.appendText("validation should result in the expected errors"); 
    } 
} 

這段代碼重複行由行:

Validator validator = new Validator(item); 
List<ValidationMessage> errorMessages = validator.getErrorMessageGroup() 
     .getMessages(); 
Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage()) 
     .collect(Collectors.toSet()); 
Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet()); 
Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages); 
Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages); 

我可以通過包裹這片的方法中的或λ表達式拋開重複的(返回一個對設置或接受作爲參數的函數來計算布爾值或字符串我需要)但理想情況下,我想只能執行這一次

我需要item弄清楚兩個matchesSafely的結果和describemisMatchSafely輸出消息,但每次它作爲參數傳遞。這不是靜態方法hasValidationErrors的參數,所以我看不到一個乾淨的方法來將結果緩存在幾個變量中。

我可能在這些方法之一中執行此代碼並將其緩存在字段中,但the Javadoc for TypeSafeMatcher似乎無法保證首先執行哪種方法。

回答

0

如果我明白你想要做什麼,那麼你正在尋找由TypeSafeDiagnosingMatcher提供的功能。嘗試擴展而不是TypeSafeMatcher

return new TypeSafeDiagnosingMatcher<ValidationSubject>() { 

    @Override 
    protected boolean matchesSafely(ValidationSubject item, Description mismatchDescription) { 
     // this calls the relevant methods on 'item' internally 
     Validator validator = new Validator(item); 
     List<ValidationMessage> errorMessages = validator.getErrorMessageGroup() 
       .getMessages(); 
     Set<String> actualMessages = errorMessages.stream().map(e -> e.getMessage()) 
       .collect(Collectors.toSet()); 
     Set<String> expectedMessages = Stream.of(expectedErrors).collect(Collectors.toSet()); 
     Set<String> missingMessages = SetUtils.difference(expectedMessages, actualMessages); 
     Set<String> unexpectedMessages = SetUtils.difference(actualMessages, expectedMessages); 
     mismatchDescription.appendText("Validation errors were missing or unexpected\n") 
       .appendValueList("\tSuperfluous messages: ", ", ", "\n", unexpectedMessages.toArray()) 
       .appendValueList("\tMissing messages: ", ", ", "\n", missingMessages.toArray()); 
     return SetUtils.union(unexpectedMessages, missingMessages).isEmpty(); 
    } 

    @Override 
    public void describeTo(Description description) { 
     description.appendText("validation should result in the expected errors"); 
    } 
}