web-dev-qa-db-fra.com

Tests unitaires avec mockito pour les constructeurs

J'ai une classe.

Class First {

    private Second second;

    public First(int num, String str) {
        second = new Second(str);
        this.num = num;
    }

    ... // some other methods
}

Je veux écrire des tests unitaires pour les méthodes publiques de la classe First. Je veux éviter l'exécution du constructeur de la classe Second.

J'ai fait ça:

Second second = Mockito.mock(Second.class);
Mockito.when(new Second(any(String.class))).thenReturn(null);
First first = new First(null, null);

Il appelle toujours le constructeur de la classe Second. Comment puis-je l'éviter?

33
Tarun Kumar

Une fois encore, le problème des tests unitaires provient de la création manuelle d'objets à l'aide de l'opérateur new. Pensez plutôt à passer Second déjà créé:

class First {

  private Second second;

  public First(int num, Second second) {
    this.second = second;
    this.num = num;
  }

  // some other methods...
}

Je sais que cela pourrait signifier une réécriture majeure de votre API, mais il n'y a pas d'autre moyen. Aussi cette classe n'a aucun sens:

Mockito.when(new Second(any(String.class).thenReturn(null)));

Tout d'abord, Mockito ne peut que se moquer des méthodes, pas des constructeurs. Deuxièmement, même si vous pouviez vous moquer de constructeur, vous vous moquez d’un objet que vous venez de créer et vous ne faites jamais rien avec cet objet.

25
Tomasz Nurkiewicz

Vous pouvez utiliser PowerMockito

Voir l'exemple:

Second second = Mockito.mock(Second.class);
whenNew(Second.class).withNoArguments().thenReturn(second);

Mais la refactorisation est une meilleure décision.

61
terma

Voici le code pour simuler cette fonctionnalité à l'aide de l'API PowerMockito.

Second mockedSecond = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mockedSecond);

Vous devez utiliser Powermockito Runner et ajouter les classes de test requises (séparées par des virgules) qui doivent être simulées par l'API powermock.

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class,Second.class})
class TestClassName{
    // your testing code
}
13
Rocky Mena

J'ai utilisé "Modèle 2 - le" modèle d'assistance d'usine "

Modèle 2 - le modèle d'assistance d'usine

Un cas où ce modèle ne fonctionnera pas est si MyClass est final. La plupart du framework Mockito ne joue pas particulièrement bien avec les classes finales; et cela inclut l'utilisation de spy (). Un autre cas est celui où MyClass utilise getClass () quelque part et requiert que la valeur résultante soit MyClass. Cela ne fonctionnera pas, car la classe d'un espion est en réalité une sous-classe de la classe d'origine générée par Mockito.

Dans l'un ou l'autre de ces cas, vous aurez besoin du modèle d'assistance d'usine légèrement plus robuste, comme suit.

public class MyClass{
  static class FactoryHelper{
      Foo makeFoo( A a, B b, C c ){
          return new Foo( a, b, c );
      }
  }

  //...

  private FactoryHelper helper;
  public MyClass( X x, Y y ){
      this( x, y, new FactoryHelper());
  } 

  MyClass( X x, Y, y, FactoryHelper helper ){

      //...

      this.helper = helper;
  } 

  //...

  Foo foo = helper.makeFoo( a, b, c );
}

Donc, vous avez un constructeur spécial, juste pour tester, qui a un argument supplémentaire. Ceci est utilisé à partir de votre classe de test lors de la création de l'objet que vous allez tester. Dans votre classe de test, vous vous moquez de la classe FactoryHelper, ainsi que de l'objet que vous souhaitez créer.

@Mock private MyClass.FactoryHelper mockFactoryHelper;
@Mock private Foo mockFoo;
private MyClass toTest;

et vous pouvez l'utiliser comme ça

toTest = new MyClass( x, y, mockFactoryHelper ); 
when( mockFactoryHelper.makeFoo( 
  any( A.class ), any( B.class ), any( C.class )))
  .thenReturn( mockFoo ); 

Source: http://web.archive.org/web/20160322155004/http://code.google.com/p/mockito/wiki/MockingObjectCreation

4

Je crois qu'il n'est pas possible de se moquer des constructeurs avec Mockito. Au lieu de cela, je suggère l'approche suivante

   Class First {

            private Second second;

            public First(int num, String str) {
            if(second== null){
            //when junit runs, you get the mocked object(not null), hence don't 
            //initialize            
           second = new Second(str);
           }
                this.num = num;
            }

        ... // some other methods
    }





    class TestFirst{
        @InjectMock
        First first;//inject mock the real testable class
        @Mock
        Second second
    testMethod(){

    //now you can play around with any method of the Second class using its 
    //mocked object(second),like:
    when(second.getSomething(String.class)).thenReturn(null);
        }
0
Rishabh Sachdeva