Mockito: méthodes de stubbing qui renvoient le type avec des jokers délimités

135

Considérez ce code:

public class DummyClass {
    public List<? extends Number> dummyMethod() {
        return new ArrayList<Integer>();
    }
}
public class DummyClassTest {
    public void testMockitoWithGenerics() {
        DummyClass dummyClass = Mockito.mock(DummyClass.class);
        List<? extends Number> someList = new ArrayList<Integer>();
        Mockito.when(dummyClass.dummyMethod()).thenReturn(someList); //Compiler complains about this
    }
}

Le compilateur se plaint de la ligne qui tente de stuber le comportement dummyMethod(). Des pointeurs sur la façon dont on procède aux méthodes de stubbing qui retournent un type avec des jokers bornés?

Shikhar Mishra
la source
Pouvez-vous mettre à jour votre extrait de code pour afficher les types génériques?
moulin du
1
Terminé. J'ai dû supprimer les balises de pré et de code, elles étaient supprimées <? étend Number> à partir de la déclaration de type.
Shikhar Mishra

Réponses:

190

Vous pouvez également utiliser la méthode sûre non-type doReturn à cet effet,

@Test
public void testMockitoWithGenerics()
{
    DummyClass dummyClass = Mockito.mock(DummyClass.class);
    List<? extends Number> someList = new ArrayList<Integer>();

    Mockito.doReturn(someList).when(dummyClass).dummyMethod();

    Assert.assertEquals(someList, dummyClass.dummyMethod());
}

comme indiqué sur le groupe Google de Mockito.

Bien que ce soit plus simple que thenAnswer, notez à nouveau que ce n'est pas un type sûr. Si vous êtes préoccupé par la sécurité des types, la réponse de Millhouse est correcte.

Détails supplémentaires

Pour être clair, voici l'erreur du compilateur observée,

The method thenReturn(List<capture#1-of ? extends Number>) in the type OngoingStubbing<List<capture#1-of ? extends Number>> is not applicable for the arguments (List<capture#2-of ? extends Number>)

Je crois que le compilateur a attribué le premier type générique pendant l' whenappel et ne peut pas confirmer que le deuxième type générique dans l' thenReturnappel est le même.

Il semble thenAnswerne pas rencontrer ce problème car il accepte un type générique alors qu'il thenReturnprend un type non générique, qui doit être capturé. De Mockito's OngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
la source
cela m'aide aussi en partie ... mais que se passe-t-il si la liste que vous comptez renvoyer n'est pas vide?
ttati
au lieu d'avoir une liste vide, vous pouvez également faire: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati
32

Je suppose que vous voulez pouvoir charger someListavec certaines valeurs connues; voici une approche qui utilise Answer<T>avec une méthode d'assistance basée sur un modèle pour que tout soit sûr de type:

@Test
public void testMockitoWithGenericsUsingAnswer()
{
    DummyClass dummyClass =  Mockito.mock(DummyClass.class);

    Answer<List<Integer>> answer = setupDummyListAnswer(77, 88, 99);
    Mockito.when(dummyClass.dummyMethod()).thenAnswer(answer);

    ...
}

private <N extends Number> Answer<List<N>> setupDummyListAnswer(N... values) {
    final List<N> someList = new ArrayList<N>();

    someList.addAll(Arrays.asList(values));

    Answer<List<N>> answer = new Answer<List<N>>() {
        public List<N> answer(InvocationOnMock invocation) throws Throwable {
            return someList;
        }   
    };
    return answer;
}
moulin
la source
17

J'ai frappé la même chose hier. Les deux réponses de @ nondescript1 et @millhouse m'ont aidé à trouver une solution de contournement. J'ai à peu près utilisé le même code que @millhouse, sauf que je l'ai rendu légèrement plus générique, car mon erreur n'était pas causée par un java.util.List, mais le com.google.common.base.Optional. Ma petite méthode d'aide permet donc tout type Tet pas seulement List<T>:

public static <T> Answer<T> createAnswer(final T value) {
    Answer<T> dummy = new Answer<T>() {
        @Override
        public T answer(InvocationOnMock invocation) throws Throwable {
            return value;
        }
    };
    return dummy;
}

Avec cette méthode d'aide, vous pouvez écrire:

Mockito.when(dummyClass.dummyMethod()).thenAnswer(createAnswer(someList));

Cela compile très bien et fait la même chose que la thenReturn(...)méthode.

Est-ce que quelqu'un sait si l'erreur émise par le compilateur Java est un bogue du compilateur ou si le code est vraiment incorrect?

Marek Radonsky
la source
Cela semble direct, simple et, pour autant que je sache, correct. Je ne sais pas pourquoi Mockito ne fournit pas quelque chose de similaire ... à moins qu'il ne le fasse?
vacao
14
Dans Java 8, il peut être raccourci Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)
:,
1
@fikovnik Quelle belle découverte "thenAnswer"!
borjab
5

Je tourne le commentaire de fikovnik en réponse ici pour lui donner plus de visibilité car je pense que c'est la solution la plus élégante utilisant Java 8+.

La documentation Mockito recommande d'utiliserdoReturn() (comme suggéré dans la réponse acceptée) qu'en dernier recours.

Au lieu de cela, pour contourner l'erreur du compilateur décrite dans la question, l' when()approche Mockito recommandée peut être utilisée avec thenAnswer()et un lambda (au lieu d'une méthode d'assistance):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
un autre nœud
la source
bien que cela ne donne aucune erreur de compilation, la liste retournée est vide même lorsque nous passons une liste avec des entrées.
Venkatesh Kolla - user2742897
0

Bien que la méthode utilitaire proposée par Marek Radonsky fonctionne, il existe également une autre option qui ne nécessite même pas l'expression lambda (à mon humble avis étrange) suggérée par fikovnik:

Comme le montre cette réponse à une question similaire, vous pouvez également utiliser ce qui suit:

BDDMockito.willReturn(someList).given(dummyClass).dummyMethod();
Andreas Siegel
la source