web-dev-qa-db-fra.com

Mockito: méthodes de stubbing qui renvoient des types avec des caractères génériques délimités

Considérons 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 d'empêcher le comportement de dummyMethod(). Des indications sur la manière dont on utilise les méthodes de talonnage qui renvoient un type avec des caractères génériques liés?

108
mrmsra

Vous pouvez également utiliser la méthode non sécurisée par le type doReturn à cette fin,

@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 discuté sur le groupe google de Mockito.

Bien que cela soit plus simple que thenAnswer, notez à nouveau qu’il n’est pas sûr de taper. Si vous êtes préoccupé par la sécurité du type, le paramètre answer est correct.

Détails supplémentaires

Pour être clair, voici l'erreur de compilation 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 de caractère générique lors de l'appel when et ne peut ensuite pas confirmer que le deuxième type de caractère générique dans l'appel thenReturn est identique.

Il semble que thenAnswer ne rencontre pas ce problème car il accepte un type de caractère générique alors que thenReturn prend un type non générique, qui doit être capturé. De Mockito En cours de Stubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
159
John McCarthy

Je suppose que vous voulez pouvoir charger someList avec des valeurs connues; voici une approche qui utilise Answer<T> avec une méthode d’aide basée sur un modèle pour tout garder en sécurité:

@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;
}
29
millhouse

Je frappe 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 légèrement rendu plus générique, car mon erreur n'était pas causée par un Java.util.List, Mais par le com.google.common.base.Optional. Ma petite méthode d'assistance permet donc tout type T et 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'assistance, vous pouvez écrire:

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

Ceci compile parfaitement et fait la même chose que la méthode thenReturn(...).

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

14
Marek Radonsky

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

Le documentation Mockito recommande d'utiliser doReturn() (comme suggéré dans la réponse acceptée) uniquement en dernier recours.

Pour éviter l'erreur de compilation décrite dans la question, l'approche recommandée par Mockito when() peut être utilisée avec thenAnswer() et une méthode lambda (au lieu d'une méthode d'assistance):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
1
anothernode

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

Comme cette réponse à une question similaire, vous pouvez également utiliser les éléments suivants:

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