mardi 10 février 2015

Stubber des classes dans une chaine d'appel pour cloisonner les test d'intégration

Problème

Dans le code applicatif une classe qui faisait un appel web service vers une autre application. 

Je voulais tester ma méthode a travers un test d'intégration sans pour autant faire l'appel réel vers le web service afin de ne pas créer de couplage pendant l’exécution de mon test.

J'ai voulu utiliser Mockito et PowerMock afin de stubber la classe mais cette classe était utilisée très loin dans la chaine d'appel (Le TI que je faisait était de haut niveau). 

Mon test était donc dépendant du service distant.

Solution

Exclure les classes gênantes

J'ai exclu dans ShrinkWrap les classes gênantes dans la construction de l'archive java pour Arquillian : 

package fr.ftravaglia;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Filters;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.runner.RunWith;

import fr.ftravaglia.RemoteServiceMails;
import fr.ftravaglia.RemoteServiceAdresses;


@RunWith(Arquillian.class)
public class ArquillianInit {
  
 @Deployment
 public static final JavaArchive createArchive() {
  return ShrinkWrap
    .create(JavaArchive.class, "demo-ftravaglia.jar")
    .addPackages(true, Filters.exclude(RemoteServiceMails.class, RemoteServiceAdresses.class), "fr.ftravaglia")
    .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml")
    .addAsResource("META-INF/persistence.xml",
      "META-INF/persistence.xml");
 }
}

Ajouter les stubs coté test

Dans la partie test du projet (src/test/java) j'ai crée deux classe remplaçant celle qui ont été exclues.
Dans mon projet j'utilise CDI. Les classes injectés le sont a travers des qualifiers car les instances sont représenté par des interfaces pour l'IOC.

Voici une des classes (la deuxième a la même structure) 


package fr.ftravaglia;

import fr.ftravaglia.RemoteServiceMailsEAO;
import fr.ftravaglia.RemoteServiceMailsImplWS.RemoteServiceMailsQualifier;

/**
 * Use for not call remote app.
 */
@RemoteServiceMailsQualifier
public class RemoteServiceMailsMock implements RemoteServiceMailsEAO {

 /**
  * Default constructor
  */
 public RemoteServiceMailsMock() {
  super();
 }
 
 /**
  * {@inheritDoc}
  */
 @Override
 public boolean isMailValid(String mail){
  boolean result = false;

  if (mail != null && !mail.isEmpty()) {
   result = true;
  }
  return result;
 }

}

Dans le code applicatif réel l'injection se fait par :

@Inject
@RemoteServiceMailsQualifier
private transiant RemoteServiceMailsEAO serviceMailEAO;

Du coup lorsque d'Arquillian se lance et publie l'application dans le serveur d'application embarqué, les classes sont supprimés de l'archive et celles définies en tant que stubs sont utilisés a la place des classes réelles.

En cas de questions n'hésitez pas ;)