Mockito: métodos de troquelado que devuelven el tipo con comodines delimitados

135

Considera este código:

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
    }
}

El compilador se queja de la línea que está tratando de impedir el comportamiento dummyMethod(). ¿Alguna sugerencia sobre cómo se aplican los métodos de troquelado que devuelven un tipo con comodines delimitados?

Shikhar Mishra
fuente
¿Puedes actualizar tu fragmento de código para mostrar los tipos genéricos?
molino
1
Hecho. Tuve que eliminar las etiquetas pre y code, se estaban quitando <? extiende Número> desde la declaración de tipo.
Shikhar Mishra

Respuestas:

190

También puede utilizar el método seguro no tipo doReturn para este propósito,

@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());
}

como se discutió en el grupo de Google de Mockito.

Si bien esto es más simple que thenAnswer, tenga en cuenta que no es de tipo seguro. Si le preocupa la seguridad de los tipos, la respuesta de millhouse es correcta.

Detalles adicionales

Para ser claros, aquí está el error observado del compilador,

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

Creo que el compilador ha asignado el primer tipo de comodín durante la whenllamada y luego no puede confirmar que el segundo tipo de comodín en la thenReturnllamada sea el mismo.

Parece que thenAnswerno se encuentra con este problema porque acepta un tipo de comodín mientras thenReturntoma un tipo de comodín, que debe ser capturado. De Mockito's OngoingStubbing ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
fuente
esto también me ayuda en parte ... pero ¿qué sucede si la lista que espera devolver no está vacía?
ttati
en lugar de tener una lista vacía, puede hacer lo siguiente: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati
32

Supongo que desea poder cargar someListalgunos valores conocidos; Aquí hay un enfoque que se utiliza Answer<T>junto con un método auxiliar con plantilla para mantener todo seguro de tipo:

@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;
}
casa del Molino
fuente
17

Golpeé lo mismo ayer. Ambas respuestas de @ nondescript1 y @millhouse me ayudaron a encontrar una solución alternativa. He usado el mismo código que @millhouse, excepto que lo hice un poco más genérico, porque mi error no fue causado por un java.util.List, sino por el com.google.common.base.Optional. Mi pequeño método de ayuda, por lo tanto, permite cualquier tipo Ty no solo 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;
}

Con este método de ayuda podrías escribir:

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

Esto compila muy bien y hace lo mismo que el thenReturn(...)método.

¿Alguien sabe si el error que emite el compilador de Java es un error del compilador o si el código es realmente incorrecto?

Marek Radonsky
fuente
Esto parece sencillo, simple y, por lo que puedo decir, correcto. No estoy seguro de por qué Mockito no proporciona algo similar a esto ... ¿a menos que lo haga?
vacao
14
En Java 8 se puede acortar: Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList)por lo que no es necesario el método de utilidad
fikovnik el
1
@fikovnik ¡Qué gran descubrimiento "thenAnswer"!
borjab
5

Estoy convirtiendo el comentario de fikovnik en una respuesta aquí para darle más visibilidad, ya que creo que es la solución más elegante con Java 8+.

La documentación de Mockito recomienda usar doReturn()(como se sugiere en la respuesta aceptada) solo como último recurso.

En cambio, para evitar el error del compilador descrito en la pregunta, when()se puede usar el enfoque Mockito recomendado con thenAnswer()una lambda (en lugar de un método auxiliar):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
otro nodo
fuente
aunque no da ningún error en tiempo de compilación, la lista devuelta está vacía incluso cuando pasamos una lista con entradas.
Venkatesh Kolla - usuario2742897
0

Aunque el método de utilidad propuesto por Marek Radonsky funciona, también existe otra opción que ni siquiera requiere la expresión lambda (en mi humilde opinión) sugerida por fikovnik:

Como muestra esta respuesta a una pregunta similar, también puede usar lo siguiente:

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