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.
3 Réponses :
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.
Petite amélioration: @SuppressWarnings ("non coché") public static
. 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
sans castings ni avertissements.
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)); }
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
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 essayezMockito.> espion (...)