Pages

vendredi 11 juillet 2014

Mockito, les choses à savoir sur le stubbing, le verify (2/3)

Dans le précédent article nous avons vu comment initialiser les mocks. Il est temps de voir comment les utiliser concrètement.

Notre classe de tests ressemble pour le moment à ça :

public class BlogArticleServiceImplTest {
    @InjectMocks
    private BlogArticleService blogArticleService = new BlogArticleServiceImpl();
    @Mock
    private BlogExporterService mockElogExporterService;

    @Before
    public void setUp(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void writeArticleShouldPublishAndBroadCast(){
    }
}


Organisation d'un test 
Votre test Mockito s'organisera de cette manière
  • on indique comment se comportent nos mocks 
  • on appelle la méthode à tester 
  • on vérifie les résultats 
@Test
public void writeArticleShouldPublishAndBroadCast(){
   //On prepare le mock
   when(mockElogExporterService.socialBroadcast(any(Article.class)))
            .thenReturn(3);
   //On appelle notre classe
   boolean result = blogArticleService.write("title", "my new content");
   //On vérifie que le mock a ete appele
   verify(mockElogExporterService).publish(any(Article.class));
   verify(mockElogExporterService).socialBroadcast(any(Article.class));
   assertThat(result).isEqualTo(true);
}

Le stubbing
Mockito vous permet de créer des mocks, qui simulent le traitement des objets qui sont utilisés par la classe que vous voulez tester. Par défaut Mockito ne fait rien sur ces méthodes. On dit qu'elles ne sont pas stubbées (je reprends le terme de stub que l'on retrouve dans tous les articles en anglais). Vous avez un proxy qui vous renvoie une valeur correspondant à la nature de la donnée. Pour être plus clair, si votre méthode testée appelle une méthode d'un mock qui retourne normalement un int/Integer, Mockito renverra par défaut 0. Si le type est boolean/Boolean, la valeur par défaut est false. Dans les autres cas vous aurez null.

Dans l'exemple ci dessus je précise ce qu'il se passe lorsque j'appelle la méthode socialBroadcast
when(mockElogExporterService.socialBroadcast(any(Article.class)))
et je "stube" cette méthode pour préciser ce qu'elle retournera quand elle sera utilisée
.thenReturn(3);

Vous pouvez également modifier le retour ("stub_er") d'une méthode pour lui faire retourner une exception
  • méthode renvoyant une valeur
   when(mockElogExporterService.socialBroadcast(any(Article.class)))
            .thenThrow(new PublicationException("error"));
  • méthode void

   doThrow(new PublicationException("error"))
            .when(mockElogExporterService).publish(any(Article.class));

Si votre traitement ne se soucie pas de la valeur retournée ne vous embêtez pas à "stuber" les méthodes.

Dans mon cas le test pourrait être
@Test
public void writeArticleShouldPublishAndBroadCast(){
   //On appelle notre classe
   boolean result = blogArticleService.write("title", "my new content");
   //On vérifie que le mock a ete appele
   verify(mockElogExporterService).publish(any(Article.class));
   verify(mockElogExporterService).socialBroadcast(any(Article.class));
   assertThat(result).isEqualTo(true);
}

Vérification des interactions
Une fois que le mock est créé, toutes les interactions sont tracées. Vous pouvez ainsi vérifier si votre mock a été appelé ou non.
verify(mockElogExporterService).publish(any(Article.class));
verify(mockElogExporterService).socialBroadcast(any(Article.class));

Je ne trouve pas opportun de vérifier toutes les interactions. Car pour améliorer la maintenabilité d'un test dans le temps, il est préférable de faire des tests boites noires sans ce soucier du code à l'intérieur. Ceci permet de revoir votre implémentation sans avoir à reprendre sans cesse vos tests.

Mon code devient

@Test
public void writeArticleShouldPublishAndBroadCast(){
   //On appelle notre classe
   assertThat(blogArticleService.write("title", "my new content").isEqualTo(true);
}

Une question.... Qui a dit qu'écrire des tests utilisant des mocks était compliqué ?

Bon dans certains cas il est intéressant de vérifier certaines interactions. Par exemple si mon article n'est pas publier je peux vouloir être sûr qu'il n'a pas été envoyé aux réseaux sociaux

@Test
public void writeArticleShouldNotBroadCastWhenArticleIsNotPublished(){
   //On prepare le mock pour qu'il retourne une exception
   doThrow(new PublicationException("error"))
            .when(mockElogExporterService).publish(any(Article.class));
   //On appelle notre classe
   boolean result = blogArticleService.write("title", "my new content");
   //On vérifie le résultat
   verify(mockElogExporterService, never()).socialBroadcast(any(Article.class));
   assertThat(result).isEqualTo(false);
}


Nous savons maintenant comment initier, paramétrer et vérifier les mocks. Ces éléments vous permettront de vous débrouiller dans la plupart des cas. Mais il reste des choses à dire sur les mocks concernant BDD, les spy... Nous verrons ça dans un dernier article sur Mockito.

Merci à Agnes Crepet pour sa relecture

Aucun commentaire:

Enregistrer un commentaire

Remarque : Seul un membre de ce blog est autorisé à enregistrer un commentaire.