2017-05-30 1 views
1

Ich habe einen benutzerdefinierten Hamcrest-Matcher für eine Schnittstelle erstellt, die ich verwende.Zwischenspeichern von Variablen in einem benutzerdefinierten Hamcrest-Matcher

Die Matcher ist eine Instanz von TypeSafeMatcher und überschreibt die folgenden drei Methoden:

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

Die Klasse I‘ m matching behandelt die Validierung eines bestimmten Typs o f Objekte. Es kommt von einer externen Bibliothek, so dass ich es nicht einfach ändern kann. Nennen wir diese Klasse ValidationSubject

Jede Instanz von ValidationSubject diese Klasse definiert einige Logik hinter der Validierung durchgeführt werden. Dies wird durch ValidationSubject#validate(ValidationData validationData) Umsetzung getan, wo validationData ist ein Builder-Typ-Objekt, das den Programmierer Fehler zu melden Validierung ermöglicht es, basierend auf dem Zustand eines Objekt einer Klasse Umsetzung 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 
     */ 
    } 
} 

ich meine Matcher mit der testen Validierungslogik implementiert in jeder konkreten Klasse wie Foo.

Um dies zu tun, müsste ich eine Instanz von ValidationData in jedem Testfall stub/spotten/spionieren und sehen, wie sich der Zustand des ValidationData Objekts basierend auf der Logik des Testobjekts geändert hat. Das ist eine Menge Standard. Ich mag, dass meine Matcher zu abstrahieren, die

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

In diesem Fall, was ich wirklich passend gegen die Argumente der hasValidationErrors Matcher eine Reihe von String-Wert ist, der Gegenstand unter Test im ValidationData Objekt gespeichert.

Das Extrahieren dieser Werte erfordert etwas Code.

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"); 
    } 
} 

Dieses Stück Code Zeile-für-Zeile wiederholt wird:

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); 

ich durch Umwickeln dieses Stück in einem Verfahren oder einem Lambda-Ausdruck (Rückkehr ein Paar der Vervielfältigung loswerden kann von Setzt oder akzeptiert als Parameter eine Funktion, um den Booleschen Wert oder den String zu berechnen, den ich benötige), aber idealerweise möchte ich nur einmal ausführen.

Ich brauche die item, um das Ergebnis der beiden matchesSafely und die Nachrichtenausgabe von describemisMatchSafely herauszufinden, aber jedes Mal, es wird als Parameter übergeben. Es ist kein Parameter der statischen Methode hasValidationErrors, so dass ich keine saubere Möglichkeit sehe, das Ergebnis in ein paar Variablen zwischenzuspeichern.

Ich könnte möglicherweise diesen Code in einer dieser Methoden ausführen und es in einem Feld zwischenspeichern, aber the Javadoc for TypeSafeMatcher scheint keine Garantien zu geben, welche Methode zuerst ausgeführt wird.

Antwort

0

Wenn ich verstehe, was Sie zu tun versuchen, suchen Sie nach Funktionalität von TypeSafeDiagnosingMatcher zur Verfügung gestellt. Versuchen Sie, das anstelle von TypeSafeMatcher zu verlängern:

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"); 
    } 
} 
Verwandte Themen