Mockito: Métodos de stubbing que retornam tipo com curingas limitados

135

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

O compilador reclama da linha para a qual está tentando stub o comportamento dummyMethod(). Alguma dica sobre como proceder para stubbing métodos que retornam um tipo com curingas limitados?

Shikhar Mishra
fonte
Você pode atualizar seu snippet de código para mostrar os tipos genéricos?
28511
1
Feito. Eu tive que remover as tags pré e código, elas estavam sendo retiradas <? estende Number> da declaração de tipo.
Shikhar Mishra 04/10/11

Respostas:

190

Você também pode usar o método seguro não-tipo doReturn para essa finalidade,

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

conforme discutido no grupo do google de Mockito.

Embora isso seja mais simples thenAnswer, observe novamente que não é do tipo seguro. Se você está preocupado com a segurança do tipo, a resposta da millhouse está correta.

detalhes adicionais

Para ficar claro, aqui está o erro observado do 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>)

Acredito que o compilador tenha atribuído o primeiro tipo de curinga durante a whenchamada e não poderá confirmar que o segundo tipo de curinga na thenReturnchamada é o mesmo.

Parece thenAnswerque esse problema não ocorre porque ele aceita um tipo de curinga e thenReturnusa um tipo não curinga, que deve ser capturado. Do OngoingStubbing de Mockito ,

OngoingStubbing<T> thenAnswer(Answer<?> answer);
OngoingStubbing<T> thenReturn(T value);
John McCarthy
fonte
isso também me ajuda parcialmente ... mas o que acontece se a lista que você espera retornar não estiver vazia?
ttati 30/08/16
em vez de ter uma lista vazia, você também pode fazer: List <Number> someList = new ArrayList <Integer> (); someList.add (aNumber);
ttati 30/08/16
32

Suponho que você queira carregar someListalguns valores conhecidos; aqui está uma abordagem que usa Answer<T>junto com um método auxiliar de modelo para manter tudo seguro para o 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 do Moinho
fonte
17

Eu bati a mesma coisa ontem. As duas respostas de @ nondescript1 e @millhouse me ajudaram a descobrir uma solução alternativa. Eu praticamente usei o mesmo código que o @millhouse, exceto que o tornei um pouco mais genérico, porque meu erro não foi causado por um java.util.List, mas pelo com.google.common.base.Optional. Meu pequeno método auxiliar, portanto, permite qualquer tipo Te não apenas 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;
}

Com este método auxiliar, você pode escrever:

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

Isso compila muito bem e faz a mesma coisa que o thenReturn(...)método.

Alguém sabe se o erro que o compilador Java emite é um bug do compilador ou se o código está realmente incorreto?

Marek Radonsky
fonte
Isso parece direto, simples e, o mais próximo que posso dizer, correto. Não sei por que o Mockito não fornece algo semelhante a este ....... a menos que ele o faça?
vacao 20/11/2015
14
Em Java 8 pode ser encurtado: Mockito.when(dummyClass.dummyMethod()).thenAnswer(x -> someList), então não há necessidade para o método de utilitário
fikovnik
1
@fikovnik Que grande descoberta "thenAnswer"!
borjab
5

Estou transformando o comentário do fikovnik em uma resposta aqui para dar mais visibilidade, pois acho que é a solução mais elegante usando o Java 8+.

A documentação do Mockito recomenda o uso doReturn()(como sugerido na resposta aceita) apenas como último recurso.

Em vez disso, para contornar o erro do compilador descrito na pergunta, a when()abordagem Mockito recomendada pode ser usada com thenAnswer()um lambda (em vez de um método auxiliar):

Mockito.when(mockedClass.mockedMethod()).thenAnswer(x -> resultList)
outro nó
fonte
embora não exista nenhum erro de tempo de compilação, a lista retornada fica vazia mesmo quando estamos passando uma lista com entradas.
Venkatesh Kolla - user2742897
0

Embora o método de utilidade proposto por Marek Radonsky funcione, há também uma outra opção que nem exige a expressão lambda (IMHO de aparência estranha) sugerida por fikovnik:

Como essa resposta a uma pergunta semelhante mostra, você também pode usar o seguinte:

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