Evitez les tests fragiles pour la couche Service

Table des matières

  • link: #overview[ 1. Overview]

  • link: #templates[ 2. Les calques]

  • link: #javaconfig[ 3. Motivation et flou des lignes de l’unité

tester] link: #conclusion[ 4. ** Conclusion]

1. Vue d’ensemble

Il existe de nombreuses façons de tester la couche de service d’une application. Le but de cet article est de montrer une façon de tester par unité cette couche en isolant totalement les interactions avec la base de données.

Cet exemple utilisera Spring pour l’injection de dépendance, JUnit, Hamcrest et http://code.google.com/ . p/mockito/[Mockito]pour les tests, mais les technologies peuvent varier.

2. Les couches

L’application Web Java typique aura une couche de service au-dessus d’une couche DAL/DAO qui appellera à son tour la couche de persistance brute.

1.1. La couche de service

@Service
public class FooService implements IFooService{

   @Autowired
   IFooDAO dao;

   @Override
   public Long create( Foo entity ){
      return this.dao.create( entity );
   }

}

1.2. La couche DAL/DAO

@Repository
public class FooDAO extends HibernateDaoSupport implements IFooDAO{

   public Long create( Foo entity ){
      Preconditions.checkNotNull( entity );

      return (Long) this.getHibernateTemplate().save( entity );
   }

}

3. Motivation et brouillage des lignes du test unitaire

Lorsque l’unité teste un service, l’unité standard est généralement le service classe ** , aussi simple que cela. Le test simulera la couche inférieure - dans ce cas, la couche DAO/DAL et vérifiera les interactions sur celle-ci. Même chose pour la couche DAO: masquer les interactions avec la base de données ( HibernateTemplate dans cet exemple) et vérifier les interactions avec cela.

C’est une approche valable, mais elle conduit à des tests fragiles - ajouter ou supprimer une couche revient presque toujours à réécrire entièrement les tests. Cela est dû au fait que les tests reposent sur la structure exacte des couches et qu’une modification de celle-ci implique une modification des tests.

Pour éviter ce type de rigidité, nous pouvons élargir la portée du test d’unité en modifiant la définition de l’unité. Nous pouvons examiner une opération persistante en tant qu’unité, de la couche de service à la DAO en passant par la procédure brute. la persistance - quoi que ce soit. Désormais, le test unitaire consommera l’API de la couche de service et la maquette de persistance brute sera simulée - dans ce cas, le HibernateTemplate :

public class FooServiceUnitTest{

   FooService instance;

   private HibernateTemplate hibernateTemplateMock;

   @Before
   public void before(){
      this.instance = new FooService();
      this.instance.dao = new FooDAO();
      this.hibernateTemplateMock = mock( HibernateTemplate.class );
      this.instance.dao.setHibernateTemplate( this.hibernateTemplateMock );
   }

   @Test
   public void whenCreateIsTriggered__thenNoException(){
     //When
      this.instance.create( new Foo( "testName" ) );
   }

   @Test( expected = NullPointerException.class )
   public void whenCreateIsTriggeredForNullEntity__thenException(){
     //When
      this.instance.create( null );
   }

   @Test
   public void whenCreateIsTriggered__thenEntityIsCreated(){
     //When
      Foo entity = new Foo( "testName" );
      this.instance.create( entity );

     //Then
      ArgumentCaptor< Foo > argument = ArgumentCaptor.forClass( Foo.class );
      verify( this.hibernateTemplateMock ).save( argument.capture() );
      assertThat( entity, is( argument.getValue() ) );
   }

}

Désormais, le test ne porte que sur une seule responsabilité: lorsque la création est déclenchée, la création atteint-elle la base de données?

Le dernier test utilise la syntaxe de vérification Mockito pour vérifier que la méthode save a été appelée sur le modèle de veille prolongée, en capturant l’argument dans le processus afin qu’il puisse également être vérifié. La responsabilité de la création de l’entité est vérifiée via ce test d’interaction, sans qu’il soit nécessaire de vérifier tout état; le test confirme que la logique de sauvegarde hibernate fonctionne comme prévu. Bien sûr, cela doit aussi être testé, mais c’est une autre responsabilité et un autre type de test.

4. Conclusion

Cette technique conduit invariablement à des tests plus ciblés, ce qui les rend plus résilients et plus flexibles au changement. La seule raison pour laquelle le test doit maintenant échouer, c’est parce que la responsabilité sous test est rompue.