我爲我使用的界面創建了一個自定義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
似乎無法保證首先執行哪種方法。