414 votes

Mockito : comment vérifier que la méthode a été appelée sur un objet créé dans une méthode ?

Je suis nouveau sur Mockito.

Étant donné la classe ci-dessous, comment puis-je utiliser Mockito pour vérifier que someMethod a été invoqué exactement une fois après foo a été invoqué ?

public class Foo
{
    public void foo(){
        Bar bar = new Bar();
        bar.someMethod();
    }
}

Je voudrais faire l'appel de vérification suivant,

verify(bar, times(1)).someMethod();

bar est une instance simulée de Bar .

2 votes

stackoverflow.com/questions/6520242/ - Mais je ne veux pas utiliser PowerMock.

0 votes

Changez l'API ou PowerMock. L'un ou l'autre.

0 votes

Comment couvrir quelque chose comme ceci ? public synchronized void start(BundleContext bundleContext) throws Exception { BundleContext bc = bundleContext ; logger.info("STARTING HTTP SERVICE BUNDLE") ; this. tracker = new ServiceTracker(bc, HttpService.class.getName(), null) { @Override public Object addingService(ServiceReference serviceRef) { httpService = (HttpService) super.addingService(serviceRef) ; registerServlets() ; return httpService ; }}}

462voto

csturtz Points 1364

Injection de dépendances

Si vous injectez l'instance Bar, ou une fabrique utilisée pour créer l'instance Bar (ou l'une des 483 autres façons de le faire), vous aurez l'accès nécessaire pour effectuer le test.

Exemple d'usine :

Étant donné une classe Foo écrite comme ceci :

public class Foo {
  private BarFactory barFactory;

  public Foo(BarFactory factory) {
    this.barFactory = factory;
  }

  public void foo() {
    Bar bar = this.barFactory.createBar();
    bar.someMethod();
  }
}

dans votre méthode de test, vous pouvez injecter une BarFactory comme ceci :

@Test
public void testDoFoo() {
  Bar bar = mock(Bar.class);
  BarFactory myFactory = new BarFactory() {
    public Bar createBar() { return bar;}
  };

  Foo foo = new Foo(myFactory);
  foo.foo();

  verify(bar, times(1)).someMethod();
}

Bonus : Il s'agit d'un exemple de la manière dont la méthode TDD peut influencer la conception de votre code.

9 votes

Existe-t-il un moyen de faire cela sans modifier la classe pour les tests unitaires ?

11 votes

Bar bar = mock(Bar.class) au lieu de Bar bar = new Bar();

8 votes

Pas à ma connaissance. Mais je ne vous suggère pas de modifier la classe juste pour les tests unitaires. C'est vraiment une conversation sur le code propre et le SRP. Ou est-ce la responsabilité de la méthode foo() dans la classe Foo de construire un objet Bar. Si la réponse est oui, alors c'est un détail d'implémentation et vous ne devriez pas vous soucier de tester l'interaction spécifiquement (se référer à la réponse de @Michael). Si la réponse est non, alors vous modifiez la classe parce que votre difficulté à tester est un signal d'alarme indiquant que votre conception a besoin d'être améliorée (d'où le bonus que j'ai ajouté sur la façon dont le TDD guide la conception).

19voto

La réponse classique est : "Vous ne le faites pas." Vous testez l'API publique de Foo et non ses organes internes.

Y a-t-il un comportement de la Foo objet (ou, moins bien, un autre objet dans l'environnement) qui est affecté par foo() ? Si oui, testez-le. Et si non, que fait la méthode ?

4 votes

Alors, qu'est-ce que vous testeriez réellement ici ? L'API publique de Foo est public void foo() où les internes sont uniquement liés au bar.

22 votes

Tester uniquement l'API publique est bien, jusqu'à ce qu'il y ait de véritables bogues avec des effets secondaires qui nécessitent des tests. Par exemple, vérifier qu'une méthode privée ferme correctement ses connexions HTTP est superflu jusqu'à ce que vous découvriez que la méthode privée est pas fermer ses connexions correctement, et cause ainsi un problème massif. À ce stade, Mockito et verify() deviennent très utiles, même si vous ne vous prosternez plus devant l'autel sacré des tests d'intégration.

0 votes

@DuffJ Je n'utilise pas Java, mais cela ressemble à quelque chose que votre compilateur ou votre outil d'analyse de code devrait détecter.

16voto

raspacorp Points 351

Si vous ne voulez pas utiliser DI ou Factories. Vous pouvez remanier votre classe d'une manière un peu plus délicate :

public class Foo {
    private Bar bar;

    public void foo(Bar bar){
        this.bar = (bar != null) ? bar : new Bar();
        bar.someMethod();
        this.bar = null;  // for simulating local scope
    }
}

Et votre classe de test :

@RunWith(MockitoJUnitRunner.class)
public class FooTest {
    @Mock Bar barMock;
    Foo foo;

    @Test
    public void testFoo() {
       foo = new Foo();
       foo.foo(barMock);
       verify(barMock, times(1)).someMethod();
    }
}

Alors la classe qui appelle votre méthode foo le fera comme ceci :

public class thirdClass {

   public void someOtherMethod() {
      Foo myFoo = new Foo();
      myFoo.foo(null);
   }
}

Comme vous pouvez le constater, en appelant la méthode de cette manière, vous n'avez pas besoin d'importer la classe Bar dans une autre classe qui appelle votre méthode foo, ce qui est peut-être ce que vous voulez.

Bien sûr, l'inconvénient est que vous permettez à l'appelant de définir l'objet Bar.

J'espère que cela vous aidera.

9 votes

Je pense que c'est un anti-modèle. Les dépendances doivent être injectées, point final. Autoriser une dépendance injectée de manière optionnelle uniquement dans le but de tester, c'est éviter intentionnellement d'améliorer le code et c'est tester intentionnellement quelque chose de différent du code qui tourne en production. Ce sont deux choses horribles à faire.

7voto

David Wallace Points 23911

Vérifiez http://code.google.com/p/mockito/wiki/MockingObjectCreation?ts=1332544670&updated=MockingObjectCreation qui donne deux façons de contourner ce problème, sans l'utilisation de PowerMock.

6voto

John B Points 17042

Oui, si vous voulez vraiment / devez le faire, vous pouvez utiliser PowerMock. Cela doit être considéré comme un dernier recours. Avec PowerMock, vous pouvez faire en sorte qu'il renvoie un objet fantaisie à partir de l'appel au constructeur. Ensuite, faites la vérification sur le mock. Cela dit, la réponse de Csturtz est la "bonne" réponse.

Voici le lien vers Construction fictive de nouveaux objets

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X