Matcher personalizzati Hamcrest

1. Introduzione

Oltre ai matcher integrati, Hamcrest fornisce anche supporto per la creazione di matcher personalizzati.

In questo tutorial, daremo un'occhiata più da vicino a come crearli e usarli. Per avere un'anteprima dei matcher disponibili, fare riferimento a questo articolo.

2. Configurazione Matchers personalizzati

Per ottenere Hamcrest, dobbiamo aggiungere la seguente dipendenza Maven al nostro pom.xml :

 org.hamcrest java-hamcrest 2.0.0.0 test 

L'ultima versione di Hamcrest può essere trovata su Maven Central.

3. Introduzione a TypeSafeMatcher

Prima di iniziare con i nostri esempi, è importante comprendere la classe TypeSafeMatcher . Dovremo estendere questa classe per creare un nostro matcher.

TypeSafeMatcher è una classe astratta, quindi tutte le sottoclassi devono implementare i seguenti metodi:

  • matchSafely (T t) : contiene la nostra logica di corrispondenza
  • descriveTo (descrizione descrizione) : personalizza il messaggio che il client riceverà quando la nostra logica di abbinamento non è soddisfatta

Come possiamo vedere nel primo metodo, TypeSafeMatcher è parametrizzato, quindi dovremo dichiarare un tipo quando lo usiamo. Questo sarà il tipo di oggetto che stiamo testando.

Rendiamolo più chiaro guardando il nostro primo esempio nella sezione successiva.

4. Creazione di onlyDigits Matcher

Per il nostro primo caso d'uso, creeremo un matcher che restituisce true se una determinata stringa contiene solo cifre.

Quindi, onlyDigits applicato a "123" dovrebbe restituire true mentre " hello1 " e " bye " dovrebbero restituire false.

Iniziamo!

4.1. Creazione di Matcher

Per iniziare con il nostro matcher, creeremo una classe che estende TypeSafeMatcher :

public class IsOnlyDigits extends TypeSafeMatcher { @Override protected boolean matchesSafely(String s) { // ... } @Override public void describeTo(Description description) { // ... } }

Tieni presente che poiché l'oggetto che testeremo è un testo, stiamo parametrizzando la nostra sottoclasse di TypeSafeMatcher con la classe String.

Ora siamo pronti per aggiungere la nostra implementazione:

public class IsOnlyDigits extends TypeSafeMatcher { @Override protected boolean matchesSafely(String s) { try { Integer.parseInt(s); return true; } catch (NumberFormatException nfe){ return false; } } @Override public void describeTo(Description description) { description.appendText("only digits"); } }

Come possiamo vedere, matchSafey sta cercando di analizzare la nostra stringa di input in un numero intero . Se riesce, restituisce true . Se fallisce, restituisce false . Risponde con successo al nostro caso d'uso.

Descrivere , invece, è allegare un testo che rappresenta le nostre aspettative. Vedremo come si manifesta in seguito quando useremo il nostro matcher.

Abbiamo solo bisogno di un'altra cosa per completare il nostro matcher: un metodo statico per accedervi , quindi si comporta come il resto dei matcher incorporati.

Quindi, aggiungeremo qualcosa di simile:

public static Matcher onlyDigits() { return new IsOnlyDigits(); }

E abbiamo finito! Vediamo come utilizzare questo matcher nella prossima sezione.

4.2. Utilizzo del Matcher

Per utilizzare il nostro nuovissimo matcher, creeremo un test :

@Test public void givenAString_whenIsOnlyDigits_thenCorrect() { String digits = "1234"; assertThat(digits, onlyDigits()); }

E questo è tutto. Questo test passerà perché la stringa di input contiene solo cifre. Ricorda che, per renderlo un po 'più leggibile, possiamo usare il matcher is che funge da wrapper su qualsiasi altro matcher :

assertThat(digits, is(onlyDigits()));

Infine, se eseguissimo lo stesso test ma con l'ingresso "123ABC", il messaggio di output sarebbe:

java.lang.AssertionError: Expected: only digits but: was "123ABC"

Questo è dove si vede il testo che abbiamo aggiunto alla describeTo metodo. Come possiamo aver notato, è importante creare una descrizione adeguata di ciò che ci si aspetta dal test.

5. divisibile per

Quindi, cosa succederebbe se volessimo creare un matcher che definisca se un numero è divisibile per un altro numero? Per quello scenario, dovremo memorizzare uno dei parametri da qualche parte.

Vediamo come possiamo farlo:

public class IsDivisibleBy extends TypeSafeMatcher { private Integer divider; // constructors @Override protected boolean matchesSafely(Integer dividend) { if (divider == 0) { return false; } return ((dividend % divider) == 0); } @Override public void describeTo(Description description) { description.appendText("divisible by " + divider); } public static Matcher divisibleBy(Integer divider) { return new IsDivisibleBy(divider); } }

Abbastanza semplice, abbiamo appena aggiunto un nuovo attributo alla nostra classe e l'abbiamo assegnato durante la costruzione . Quindi, lo abbiamo appena passato come parametro al nostro metodo statico:

@Test public void givenAnEvenInteger_whenDivisibleByTwo_thenCorrect() { Integer ten = 10; Integer two = 2; assertThat(ten,is(divisibleBy(two))); } @Test public void givenAnOddInteger_whenNotDivisibleByTwo_thenCorrect() { Integer eleven = 11; Integer two = 2; assertThat(eleven,is(not(divisibleBy(two)))); }

E questo è tutto! Abbiamo già il nostro matcher che utilizza più di un input!

6. Conclusione

Hamcrest fornisce abbinamenti che coprono la maggior parte dei casi d'uso che uno sviluppatore di solito deve affrontare quando crea asserzioni.

Inoltre, se un caso specifico non è coperto, Hamcrest fornisce anche supporto per creare abbinamenti personalizzati da utilizzare in scenari specifici, come abbiamo esplorato qui . Sono semplici da creare e vengono utilizzati esattamente come quelli inclusi nella libreria.

Per ottenere l'implementazione completa di questi esempi, fare riferimento al progetto GitHub.