Mockito corresponde a qualquer argumento de classe

153

Existe uma maneira de corresponder a qualquer argumento de classe da rotina de exemplo abaixo?

class A {
     public B method(Class<? extends A> a) {}
}

Como posso sempre retornar um, new B()independentemente de qual classe é passada method? A tentativa a seguir funciona apenas para o caso específico em que Aé correspondido.

A a = new A();
B b = new B();
when(a.method(eq(A.class))).thenReturn(b);

EDIT : Uma solução é

(Class<?>) any(Class.class)
Johan Sjöberg
fonte
6
Class<?>surpreendente!
António Almeida
Sua (Classe <?>) Qualquer solução (Classe.classe) deve ser a resposta aqui. Eu prefiro usar isso do que a classe ClassOrSubclassMatcher vista abaixo.
superbAfterSemperPhi
@superbAfterSemperPhi e johan-sjöberg Postei outra maneira de fazer isso, sem elenco. Eu acredito que poderia ser uma maneira melhor. O que você acha?
Anmaia 13/01/16

Respostas:

188

Mais duas maneiras de fazer isso (veja meu comentário na resposta anterior de @Tomasz Nurkiewicz):

O primeiro baseia-se no fato de que o compilador simplesmente não permite que você passe algo do tipo errado:

when(a.method(any(Class.class))).thenReturn(b);

Você perde a digitação exata (o Class<? extends A>), mas provavelmente funciona como você precisa.

O segundo é muito mais envolvido, mas é sem dúvida uma solução melhor se você realmente quiser ter certeza de que o argumento para method()é uma Aou uma subclasse de A:

when(a.method(Matchers.argThat(new ClassOrSubclassMatcher<A>(A.class)))).thenReturn(b);

Onde ClassOrSubclassMatcheré org.hamcrest.BaseMatcherdefinido como:

public class ClassOrSubclassMatcher<T> extends BaseMatcher<Class<T>> {

    private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @SuppressWarnings("unchecked")
    public boolean matches(Object obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom((Class<T>) obj);
            }
        }
        return false;
    }

    public void describeTo(Description desc) {
        desc.appendText("Matches a class or subclass");
    }       
}

Ufa! Eu usaria a primeira opção até que você realmente precise obter um controle mais preciso sobre o que method()realmente retorna :-)

casa do Moinho
fonte
a if (obj instanceof Class)bagunça as coisas para mim, então eu a removi.
Daniel Smith
Na linha de declaração da classe, tive que mudar extends BaseMatcher<Class<T>>para apenas extends BaseMatcher<T>. Apenas para sua informação, se alguém mais receber erros de compilação, tente isso.
Jan
Eu também tive que mudar a matchesfunção para o seguinte:public boolean matches(Object obj) { if (obj != null) { return targetClass.isAssignableFrom(obj.getClass()); } return false; }
Jan
qualquer (Class.class) está retornando null - Como posso evitar de retornar nulo
Arvind Kumar
Seria ótimo se realmente adicionar a classe que eu preciso de importação já que agora são muitos "qualquer" de Mockito
jpganz18
53

Há outra maneira de fazer isso sem elenco:

when(a.method(Matchers.<Class<A>>any())).thenReturn(b);

Esta solução força o método any()a retornar o Class<A>tipo e não seu valor padrão ( Object).

anmaia
fonte
5
Matchersfoi descontinuado nas versões mais recentes do Mockito e provavelmente será removido na versão 3.0. Em ArgumentMatchersvez disso, use :when(a.method(ArgumentMatchers.<Class<A>>any())).thenReturn(b);
Voicu
41

Se você não tem idéia de qual pacote precisa importar:

import static org.mockito.ArgumentMatchers.any;
any(SomeClass.class)

OU

import org.mockito.ArgumentMatchers;
ArgumentMatchers.any(SomeClass.class)
João Luiz Cadore
fonte
13
Isso salvou minha vida, acidentalmente estava importando "qualquer" da biblioteca hamcrest.
Gábor Nagy
3
Agora foi alterado paraorg.mockito.ArgumentMatchers.any
BOWS
27

E se:

when(a.method(isA(A.class))).thenReturn(b);

ou:

when(a.method((A)notNull())).thenReturn(b);
Tomasz Nurkiewicz
fonte
4
Estes seriam compilar e trabalho, se a assinatura do método foi method(A a)- mas é (efetivamente) method(Class<A> a)- assim você precisa usar: when(a.method(isA(Class.class))).thenReturn(b);ouwhen(a.method((Class<A>) notNull())).thenReturn(b);
millhouse
segunda parte para mim funciona como charme. lutar com qualquer (SomeClass.class) leva a um beco sem saída. Mas (SomeClass.class) notNull () salvou meu dia
Vadim
Se você tiver dois métodos com o mesmo nome, mas com argumentos diferentes, poderá desambiguar o método a ser ridicularizado usando a segunda versão aqui. A primeira versão não foi adequada para mim (no Java 8).
Patru
Obrigado, isA (A.class) funciona bem para mim e o mvcConversionService seleciona a classe certa. Isso não estava funcionando com nenhum (classe A.) e eq (classe A.).
D3rbastl3r 9/07/19
9

a solução da millhouse não está mais funcionando com a versão recente do mockito

Esta solução funciona com java 8 e mockito 2.2.9

onde ArgumentMatcheré uma instância deorg.mockito.ArgumentMatcher

public class ClassOrSubclassMatcher<T> implements ArgumentMatcher<Class<T>> {

   private final Class<T> targetClass;

    public ClassOrSubclassMatcher(Class<T> targetClass) {
        this.targetClass = targetClass;
    }

    @Override
    public boolean matches(Class<T> obj) {
        if (obj != null) {
            if (obj instanceof Class) {
                return targetClass.isAssignableFrom( obj);
            }
        }
        return false;
    }
}

E o uso

when(a.method(ArgumentMatchers.argThat(new ClassOrSubclassMatcher<>(A.class)))).thenReturn(b);
Bertrand Cedric
fonte
a condição instanceof não é mais necessária e eu escrevi um método de conveniência:public static <T> Class<T> subClassOf(Class<T> targetClass) { return argThat(new ClassOrSubclassMatcher<>(targetClass)); }
Daniel Alder