Pages

mercredi 9 juillet 2014

Comment utiliser des mocks simplement avec Mockito (1/3)

Je n'ai pas fait beaucoup d'articles sur les mocks mais j'observe depuis plusieurs mois des remises en question de certaines pratiques de tests, comme le TDD, les mocks...

Au début des années 2000 j'ai connu une période où on ne faisait pas de test unitaire. La qualité des projets s'en faisait ressentir : période de stabilisation en production importante, évolutions risquées car souvent accompagnées de régressions, temps de développement élevé car on attendait de lancer un écran pour tester toute la logique derrière.... Les tests unitaires ont révolutionné ma vie de développeur. Aujourd'hui avec les tests unitaires je suis plus productif (bugs anticipés), mon travail est de meilleure qualité (tous les cas aux limites sont vérifiés), et je n'ai pas peur de faire du refactoring car les tests permettent de limiter les régressions. Certes il ne faut pas être extrémiste. Viser 100% de couverture du code par les tests est illusoire, et une trop forte couverture pose plus de problème qu'elle n'apporte d'avantages. Il est important de tester unitairement mais aussi de faire des tests d'intégration, d'installation....

Je ne vais pas revenir sur le débat sur les tests. Si vous êtes intéressés je vous laisse écouter l'épisode 105 des Cast Codeurs fait par les Duchess. Le but de cet article est plutôt de montrer comment Mockito a simplifié l'écriture des tests en Java utilisant des mocks.

Mockito a un très beau logo mais n'est pas réservé aux fans de mojito!

Le mieux est de prendre un exemple construit sur la stack Java 8, Mockito 1.9 et Spring 4. Les sources exposées ici sont disponibles sous github. Nous allons nous intéresser au test d'une classe métier ayant le contrat de service suivant

public interface BlogArticleService {
    boolean write(String title, String content);
    boolean checkArticle(Article article);
}

Nous allons écrire un test de la méthode write. Il n'y a aucun intérêt à s'attarder sur l'implémentation. La seule chose à savoir est que write fait appel à plusieurs méthodes
  • une interne checkArticle qui vérifie le contenu
  • deux externes définies par l'interface BlogExporterService 
public interface BlogExporterService {
    public void publish(Article article);
    public int socialBroadcast(Article article);
}

Il est important de mocker les dépendances de la classe que nous voulons tester afin d'avoir un test rapide qui ne dépende pas d'autres composants. On écrit donc une classe de test BlogArticleServiceImplTest. Le mock de la dépendance BlogExporterService est créée lors de l'initialisation de la classe de test.

public class BlogArticleServiceImplTest {

    private BlogArticleService blogArticleService;
    private BlogExporterService mockElogExporterService;

    @Before
    public void setUp(){
        //J'initie les mocks
        mockElogExporterService = mock(BlogExporterService.class);
        //Le service a tester est créé
        blogArticleService = new BlogArticleServiceImpl();
        ((BlogArticleServiceImpl)blogArticleService)
               .setBlogExporterService(mockElogExporterService);
    }

    @Test
    public void writeArticleShouldPublishAndBroadCast(){
    }
}

Mais vous pouvez faire plus simple. Les mocks peuvent être créés via l'annotation @Mock et injectés dans la classe à tester via l'annotation @InjectMocks. On gagne en lisibilité et je peux me passer du setter sur ma classe tester pour injecter ma dépendance.

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

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

    @Test
    public void writeArticleShouldPublishAndBroadCast(){
    }
}

Notez que Mockito permet de créer un mock aussi bien à partir d'une interface qu'une classe concrète.

    @Mock
    private BlogExporterService mockElogExporterService1;

    @Mock
    private BlogExporterServiceImpl mockElogExporterService2;

Dans cette première phase nous avons vu comment créer des mocks et les injecter dans la classe à tester. Dans le prochain article,  nous verrons comment utiliser ces mocks.

Merci à Agnes Crepet pour sa relecture




Aucun commentaire:

Enregistrer un commentaire

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