4
votes

Espionner un lambda avec mockito

J'ai rencontré un problème intéressant lors de l'écriture d'un test unitaire qui impliquait de se moquer d'un lambda.

@Test
public void spyingAnonymousImplementation() {
    final Supplier<String> spy = Mockito.spy(new Supplier<String>() {
        @Override
        public String get() {
            return "1";
        }
    });
    spy.get();
}

L'exécution de ce test échoue avec l'erreur suivante:

Mockito ne peut pas se moquer / espionner parce que: - classe finale

Une solution de contournement pour le problème ci-dessus consiste à remplacer le lambda par une implémentation anonyme:

@Test
public void spyingLambda() {
    final Supplier<String> spy = Mockito.spy((Supplier) () -> "1");
    spy.get();
}

Bien que les deux tests soient exactement les mêmes (l'EDI suggère même de remplacer le implémentation anonyme avec lambda), le deuxième test n'échoue pas.

Je me demandais s'il s'agissait d'un problème connu qui pourrait être résolu dans mockito ou s'il existe d'autres solutions de contournement.


1 commentaires

Si le cast doit être vers (Supplier ) au lieu de (Supplier), cela pourrait bien supprimer le besoin de l'invocation explicite du new . Ou peut-être essayez Mockito. > espion (...)


3 Réponses :


6
votes

Une autre façon de résoudre ce problème est la suivante:

@Test
public void spyingLambda() {
    final Supplier<String> spy = spyLambda(Supplier.class, () -> "1");
    spy.get();
}

Ce qui permet d'espionner le lambda en modifiant la première méthode comme suit:

/**
 * This method overcomes the issue with the original Mockito.spy when passing a lambda which fails with an error
 * saying that the passed class is final.
 */
public static <T> T spyLambda(final Class<T> lambdaType, final T lambda) {
    return mock(lambdaType, delegatesTo(lambda));
}

Nous espérons que les exemples ci-dessus pourront aider d'autres personnes qui rencontrent le même problème.


1 commentaires

Petite amélioration: @SuppressWarnings ("non coché") public static G spyLambda (final G lambda, final Class lambdaType) {return (G) mock (lambdaType, Deleg To (lambda)) ;} . Ici, R est le type lambda brut (par exemple Fonction ) et G est le type lambda générique (par exemple Function ). De cette façon, vous pouvez écrire quelque chose comme final Function characters = spyLambda (str -> str.length (), Function.class); sans castings ni avertissements.



3
votes

Juste à titre de référence, pour améliorer la réponse de @ alex, vous pouvez également faire

Callable<Integer> callable = spyLambda(() -> {
    return 42;
});
Supplier<Integer> supplier = spyLambda(() -> 42);
Runnable runnable = spyLambda(() -> System.out.println("42"));

et ensuite simplement l'espionner sans spécifier le type (par exemple, Supplier.class code >)

public static <T> T spyLambda(final T lambda) {
    Class<?>[] interfaces = lambda.getClass().getInterfaces();
    MatcherAssert.assertThat(interfaces, IsArrayWithSize.arrayWithSize(1));
    return Mockito.mock((Class<T>) interfaces[0], delegatesTo(lambda));
}


0 commentaires

0
votes

Vous pouvez autoriser les moqueries de la classe finale. Créez le fichier src / test / resources / mockito-extensions / org.mockito.plugins.MockMaker contenant

mock-maker-inline

https://www.baeldung.com/mockito-final#configure-mocktio


0 commentaires