Disons que je me moque de cette classe Foo
class Foo {
public void doThing(Bar bar) {
// ...
}
}
et c'est Bar
class Bar {
private int i;
public int getI() { return i; }
public void setI(int i) { this.i = i; }
}
Je sais que je peux utiliser la fonctionnalité de vérification de Mockito pour voir si Foo#doThing(Bar)
a été appelée sur la maquette avec une instance spécifique de Bar
ou n'importe quelle Bar
avec Mockito.any(Bar.class)
, mais existe-t-il un moyen de s'assurer qu'il a été appelé par n'importe quel Bar
mais avec une valeur spécifique pour i
ou Bar#getI()
?
Ce que je sais est possible:
Foo mockedFoo = mock(Foo.class);
Bar someBar = mock(Bar.class);
...
verify(mockedFoo).doThing(someBar);
verify(mockedFoo).doThing(any(Bar.class);
Ce que je veux savoir, c'est s'il existe un moyen de vérifier qu'un Bar
avec des choses particulières à ce sujet a été passé en argument.
Dans Mockito 2.1.0 et plus avec Java 8 vous pouvez passer le lambda à argThat hors de la boîte afin que l'on n'ait pas besoin d'arguments de correspondance personnalisés. Pour le exemple dans le PO serait:
verify(mockedFoo).doThing(argThat((Bar aBar) -> aBar.getI() == 5));
En effet, depuis Mockito 2.1.0, ArgumentMatcher
est une interface fonctionnelle.
Si vous utilisez Mockito 2.1.0 ou supérieur et Java 8 ou supérieur, voyez plutôt la réponse this , c'est beaucoup plus simple maintenant.
J'ai trouvé la réponse en écrivant la question.
Oui, vous pouvez. Au lieu d'utiliser any(Bar.class)
, vous devrez implémenter votre propre instance de ArgumentMatcher<T>
et utiliser Mockito#argThat(Matcher)
, par exemple, disons que nous voulons vérifier que i
est 5 ...
// in the test (could also be outside)
private static final class BarIs5 extends ArgumentMatcher<Bar> {
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == 5;
}
}
Vérifiez ensuite comme ceci: verify(mockedFoo).doThing(argThat(new BarIs5()));
Donnez un coup de pouce en ajoutant des paramètres de constructeur!
private static final class BarIsWhat extends ArgumentMatcher<Bar> {
private final int i;
public BarIsWhat(int i) {
this.i = i
}
@Override
public boolean matches(Object argument) {
return ((Bar) argument).getI() == i;
}
}
Vérifiez ensuite comme ceci: verify(mockedFoo).doThing(argThat(new BarIsWhat(5)));
Mise à jour: Ceci est apparu dans ma file d'attente à cause d'un badge et a vu une certaine marge d'amélioration.
J'ai essayé cela et cela fonctionne. Vous pouvez en quelque sorte utiliser une expression lambda qui est beaucoup plus propre (si cela ne vous dérange pas au moins des avertissements de cast non contrôlés).
Le seul problème est que argThat
accepte un Hamcrest Matcher
qui est pas a @FunctionalInterface
. Heureusement, le ArgumentMatcher
de Mockito est une classe abstraite qui l'étend et n'a qu'une seule méthode abstraite.
Dans votre test (ou un emplacement commun), créez une méthode comme ci-dessous
private static <T> ArgumentMatcher<T> matches(Predicate<T> predicate) {
return new ArgumentMatcher<T>() {
@SuppressWarnings("unchecked")
@Override
public boolean matches(Object argument) {
return predicate.test((T) argument);
}
};
}
Maintenant, dans votre test, vous pouvez le faire pour utiliser une expression lambda:
verify(mockedFoo).doThing(argThat(matches( (Bar arg) -> arg.getI() == 5 )));
Si l'utilisation de Mockito 2+ n'est pas une option, vous pouvez également utiliser le bon vieux ArgumentCaptor
. Ce sera un peu plus verbeux cependant:
ArgumentCaptor<Long> siteIdCaptor = ArgumentCaptor.forClass(Long.class);
verify(repository).findBySiteId(siteIdCaptor.capture());
assertEquals(15, siteIdCaptor.getValue().longValue());