web-dev-qa-db-fra.com

Mockito peut-il vérifier qu'un argument a certaines propriétés / champs?

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.

37
Captain Man

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.

57
Niccolò

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 )));
16
Captain Man

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());
5
yuranos87