148 votes

Se moquer des variables membres d'une classe à l'aide de Mockito

Je suis un newbie dans le développement et les tests unitaires en particulier . Je suppose que ma demande est assez simple, mais je suis curieux de savoir d'autres idées à ce sujet.

Supposons que j'ai deux classes comme le

public class First {

    Second second ;

    public First(){
        second = new Second();
    }

    public String doSecond(){
        return second.doSecond();
    }
}

class Second {

    public String doSecond(){
        return "Do Something";
    }
}

Disons que je suis d'écriture de l'unité de test pour tester First.doSecond() méthode. Cependant, supposons que, je veux Maquette Second.doSecond() de la classe comme si. Je suis à l'aide de Mockito pour ce faire.

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");

    First first = new First();
    assertEquals("Stubbed Second", first.doSecond());
}

Je vois que les moqueries ne prend pas effet et l'assertion échoue. Il n'y a aucune façon de se moquer des variables membres d'une classe que je veux tester . ?

92voto

kittylyst Points 2541

Vous devez fournir un moyen d'accéder aux variables membres afin de pouvoir passer une simulation (les méthodes les plus courantes sont une méthode setter ou un constructeur qui prend un paramètre).

Si votre code ne permet pas de le faire, il est incorrectement pris en compte pour TDD.

70voto

Janning Points 2079

Ce n'est pas possible si vous ne pouvez pas changer votre code. Mais j'aime l'injection de dépendance et Mockito le soutient:

 public class First {

    @Resource
    Second second ;

    public First(){
        second = new Second();
    }

    public String doSecond(){
        return second.doSecond();
    }
}
 

Votre test:

 @RunWith(MockitoJUnitRunner.class)
public class YourTest {
   @Mock
   Second second;

   @InjectMocks
   First first = new First();

   public void testFirst(){
      when(second.doSecond()).thenReturn("Stubbed Second");
      assertEquals("Stubbed Second", first.doSecond());
   }
}
 

C'est très gentil et facile.

39voto

soulcheck Points 17680

Si vous regardez de près votre code, vous verrez que l' second propriété dans votre essai est encore un exemple de l' Second, pas un simulacre (vous ne passez pas le bouchon first dans votre code).

La façon la plus simple serait de créer un setter pour second en First classe et la passer à la maquette explicitement.

Comme ceci:

public class First {

Second second ;

public First(){
    second = new Second();
}

public String doSecond(){
    return second.doSecond();
}

    public void setSecond(Second second) {
    this.second = second;
    }


}

class Second {

public String doSecond(){
    return "Do Something";
}
}

....

public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");


First first = new First();
first.setSecond(sec)
assertEquals("Stubbed Second", first.doSecond());
}

Une autre serait de passer un Second de l'instance en tant que Firsts'paramètre du constructeur.

Si vous ne pouvez pas modifier le code, je pense que la seule option serait d'utiliser la réflexion:

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");


    First first = new First();
    Field privateField = PrivateObject.class.
        getDeclaredField("second");

    privateField.setAccessible(true);

    privateField.set(first, sec);

    assertEquals("Stubbed Second", first.doSecond());
}

Mais vous pouvez probablement, comme il est rare de faire des tests sur le code, vous n'avez pas le contrôle (même si on peut imaginer un scénario où vous avez pour tester une bibliothèque externe car il est auteur ne l'a pas :))

7voto

user1509463 Points 21

Si vous ne pouvez pas modifier la variable membre, l’opération inverse consiste à utiliser powerMockit et à appeler

 Second second = mock(Second.class)
when(second.doSecond()).thenReturn("Stubbed Second");
whenNew(Second.class).withAnyArguments.thenReturn(second);
 

Maintenant, le problème est que TOUT appel à new Second renverra la même instance simulée. Mais dans votre cas simple cela fonctionnera.

1voto

user2707880 Points 11

Beaucoup d'autres l'ont déjà conseillé vous de revoir votre code afin de le rendre plus testable - de bons conseils et généralement plus simple que ce que je suis sur le point de suggérer.

Si vous ne pouvez pas modifier le code afin de le rendre plus testable, PowerMock: https://code.google.com/p/powermock/

PowerMock s'étend Mockito (si vous n'avez pas à apprendre une nouvelle maquette cadre), fournissant des fonctionnalités supplémentaires. Cela inclut la possibilité d'avoir un constructeur de retour d'une fantaisie. Puissant, mais un peu compliqué donc l'utiliser à bon escient.

Vous utilisez un autre se Moquer de coureur. Et vous avez besoin pour préparer la classe qui va invoquer le constructeur. (Notez que ceci est une commune gotcha - préparer la classe qui appelle le constructeur, pas de la construction de la classe)

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class})

Puis dans votre essai, vous pouvez utiliser le whenNew méthode pour avoir le constructeur de retour d'une maquette

whenNew(Second.class).withAnyArguments().thenReturn(mock(Second.class));

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