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?


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

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

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?


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)

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();
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.