web-dev-qa-db-fra.com

Stubbing d'une méthode qui prend Class <T> en paramètre avec Mockito

Il existe une méthode générique qui prend une classe en paramètre et j'ai des problèmes pour la remplacer avec Mockito. La méthode ressemble à ceci:

public <U extends Enum<U> & Error, T extends ServiceResponse<U>> T validate(
    Object target, Validator validator, Class<T> responseClass,
    Class<U> errorEnum);

C'est affreux, du moins pour moi ... J'imagine pouvoir vivre sans, mais le reste de la base de code l'utilise avec bonheur ...

J'allais, dans mon test unitaire, tronquer cette méthode pour renvoyer un nouvel objet vide. Mais comment puis-je faire cela avec mockito? J'ai essayé:

when(serviceValidatorStub.validate(
    any(), 
    isA(UserCommentRequestValidator.class), 
    UserCommentResponse.class, 
    UserCommentError.class)
).thenReturn(new UserCommentResponse());

mais étant donné que je mélange et que je fais correspondre des correspondeurs et des valeurs brutes, j'obtiens "org.mockito.exceptions.misusing.InvalidUseOfMatchersException: utilisation non valide des correspondeurs d'arguments!"

40
Peter Perháč

Le problème est que vous ne pouvez pas mélanger des paramètres d’argument et des arguments réels dans un appel simulé. Alors, faites plutôt ceci: 

when(serviceValidatorStub.validate(
    any(),
    isA(UserCommentRequestValidator.class),
    eq(UserCommentResponse.class),
    eq(UserCommentError.class))
).thenReturn(new UserCommentResponse());

Notez l'utilisation du correcteur d'arguments eq() pour faire correspondre l'égalité.

voir: https://static.javadoc.io/org.mockito/mockito-core/1.10.19/org/mockito/Matchers.html#eq(T)

En outre, vous pouvez utiliser le correcteur d'arguments same() pour les types Class<?>. Cela correspond à la même identité, à l'instar de l'opérateur Java ==.

75
Jesse

Juste pour terminer sur le même thread, si quelqu'un veut modifier une méthode qui prend une classe comme argument, mais ne se soucie pas du type, ou si plusieurs types doivent être modifiés de la même façon, voici une autre solution:

private class AnyClassMatcher extends ArgumentMatcher<Class<?>> {

    @Override
    public boolean matches(final Object argument) {
        // We always return true, because we want to acknowledge all class types
        return true;
    }

}

private Class<?> anyClass() {
    return Mockito.argThat(new AnyClassMatcher());
}

et ensuite appeler 

Mockito.when(mock.doIt(this.anyClass())).thenCallRealMethod();
2
Ash

Nice one @ Ash. J'ai utilisé votre matcher de classe générique pour préparer ci-dessous . Cela peut être utilisé si nous voulons préparer une maquette d'un type spécifique (pas d'instance)

private Class<StreamSource> streamSourceClass() {
    return Mockito.argThat(new ArgumentMatcher<Class<StreamSource>>() {

        @Override
        public boolean matches(Object argument) {
            // TODO Auto-generated method stub
            return false;
        }
    });
}

Usage:

    Mockito.when(restTemplate.getForObject(Mockito.anyString(), 
            **streamSourceClass(),**
            Mockito.anyObject));
0
user3777313