Mocking di Iterable

Nel caso in cui si volesse mockare un oggetto che implementa l’interfaccia Iterable durante il testing di un altro componente, come si potrebbe procedere?

Inizialmente si potrebbe pensare di sfruttare il thenReturn fornendo un iteratore creato ad hoc da noi, ma il problema è che utilizzando thenReturn viene creato l’oggetto solo una volta, di conseguenza il test passerebbe solo alla prima iterazione dell’iteratore mockato, dalla seconda in poi si verificherebbe un errore.

Per risolvere questo problema possiamo utilizzare il seguente codice:

public class MockUtils {
    @SafeVarargs
    public static <T> void whenIterated(Iterable<T> p, T... d) {
        when(p.iterator()).thenAnswer((Answer<Iterator<T>>) invocation -> List.of(d).iterator());
    }
}

In questo caso viene sfruttato il metodo thenAnswer che ogni volta permette di creare un nuovo iteratore con l’elemento successivo.

Questa tecnica viene utilizzata quando il SUT che sta venendo testato necessita di iterare su qualcosa per svolgere le sue funzioni. Ecco un esempio di come può essere sfruttata questa strategia:

@Test
void scegliSuccess() {
    Giocatore player1 = mock(Giocatore.class);
    Giocatore player2 = mock(Giocatore.class);
    Giocatore me = mock(Giocatore.class);

    when(player1.getMazzettoTop()).thenReturn(Rank.FIVE);
    when(player2.getMazzettoTop()).thenReturn(Rank.TEN);

    Partita partita = mock(Partita.class);
    MockUtils.whenIterated(partita, me, player2, player1);

    List<Card> mano = List.of(Card.get(Rank.ACE, Suit.CLUBS), Card.get(Rank.KING, Suit.DIAMONDS), Card.get(Rank.TEN, Suit.HEAR
    SelettoreCarta FAILED = mock(SelettoreCarta.class);
    
    SelettoreCarta strategy = new SelettoreRubaMazzetto(FAILED, me);
    
    assertThat(strategy.scegli(mano, partita)).isEqualTo(Card.get(Rank.TEN, Suit.HEARTS));
}