Thématiques principales

mardi 30 janvier 2018

Test d'IHM : fest-util

Effectuer des tests unitaires est une tâche indispensable mais qui peut souvent s'avérer un vrai casse tête lorsqu’il s’agit de traiter des IHM.

En Java standard il existe une librairie de test dédiée au IHM de type client lourd pour les API swing et awt qui s’appele Fest [1] [3]. Celle est vraiment concu pour etre simple et logique en utilisant les noms des éléments de l’IHM pour se mapper sur leur interfaces de fonctionnement.

Pour l’utiliser il suffit de tirer l'artefact suivant:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-util</artifactId>
<version>1.1.6</version>
<scope>test</scope>
</dependency>
Pour l’utiliser il reste à définir une classe de test classique JUnit héritant de la classe FestSwingJUnitTestCase dans laquelle il faudra initialiser un robot Fest et le donner à un objet de type FrameFixture qui va se charger de piloter le robot afin que celui ci parse et effectuer les interaction adéquate sur notre IHM.

Avant cela, Pour que le robot soit capable de parser notre IHM il faudra par contre indiquer a Fest sur quel IHM il doit faire ses actions (grâce a un GuiQuery). On spécifie alors une méthode statique d’initialisation dédié dans laquelle nous instancierons notre IHM:
@RunsInEDT
private static MainFrame createNewEditor(BundleContext context) {
final GuiQuery<MainFrame> query = new GuiQuery<MainFrame>() {
@Override
protected MainFrame executeInEDT() {
MainFrame app = null;
try {
app = new MainFrame(context);
} catch (final TcOsgiException e) {
e.printStackTrace();
}
return app;
}
};
return GuiActionRunner.execute(query);
}
Du coup, il nous faudra initialiser dans un setUp notre environnement de test contenant le FrameMixture:
@Override
protected void onSetUp() {
try {
BundleContext context = Mockito.mock(BundleContext.class);
editor = new FrameFixture(robot(), MainFrameTest.createNewEditor(context));
editor.show();
} catch (Exception e) {
e.printStackTrace();
}
}
Grace a cela, il ne nous restera plus qu’a declarer les actions que l’on souhaitera faire sur l’IHM dans des tests spécifiques comme on le ferait dans un test unitaire classique sauf que la nous allons scénariser les actions:
@Test
public void profilCreationSelection() {
editor.menuItem("Select MorphMath Action").click();
editor.comboBox("combo1").selectItem("dilatation").click();
editor.menuItem("GO").click();
System.out.println(new File(".").getAbsolutePath());
editor.fileChooser().fileNameTextBox().setText(
"docvierge.bmp");
editor.fileChooser().approve();
Thread.sleep(500);
editor.close();
}
A noter qu'à ce niveau d’abstraction, nous ne sommes plus vraiment en train de faire des tests unitaires mais plutôt des tests fonctionnels mais cela n’a pas forcement d’importance dans notre cas (a noter malgré tout que l’approche est particulièrement adapté à la mise en place de test pour vérifier des cas d’utilisation particulière en lien direct avec le besoin utilisateur)

Voila un petit aperçu de Fest et de ses capacités pour des tests d’IHM. Point particulier a prendre en compte lors de l'exécution des tests: ces derniers sont réalisés avec une IHM visible vous permettant de voir quels sont les actions et mouvement de souris réalisées pour faire le test. Cela est clairement intéressant mais comporte un inconvénient majeur, vos tests ne fonctionnent pas sans environnement graphique comme dans un serveur d'intégration continu. Typiquement vous aurez une erreur du type (dans Travis [2] par exemple):

PM org.fest.swing.monitor.WindowStatus <init>
WARNING: Error ocurred when creating a new Robot
java.awt.AWTException: headless environment
at java.awt.Robot.<init>(Robot.java:91)
at org.fest.swing.util.RobotFactory.newRobotInPrimaryScreen(RobotFactory.java:35)
at org.fest.swing.monitor.WindowStatus.<init>(WindowStatus.java:58)
at org.fest.swing.monitor.WindowStatus.<init>(WindowStatus.java:51)
at org.fest.swing.monitor.WindowMonitor.<init>(WindowMonitor.java:51)
at org.fest.swing.monitor.WindowMonitor$SingletonLazyLoader$1.executeInEDT(WindowMonitor.java:134)
at org.fest.swing.monitor.WindowMonitor$SingletonLazyLoader$1.executeInEDT(WindowMonitor.java:132)
at org.fest.swing.edt.GuiQuery.run(GuiQuery.java:41)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:311)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:756)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:80)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:726)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Pour résoudre ce problème il vous suffira d’utiliser xvfb (un server X11) [4] . Comme décrit dans [5], cela permet de simuler un environnement graphique. (ou un autre exemple [6]).

References:

[1] http://www.vogella.com/tutorials/FEST/article.html
[2] https://travis-ci.org/
[3] http://tuhrig.de/automated-ui-testing-with-swing-fest/
[4] https://www.x.org/archive/X11R7.6/doc/man/man1/Xvfb.1.xhtml
[5] https://docs.travis-ci.com/user/gui-and-headless-browsers/#Using-xvfb-to-Run-Tests-That-Require-a-GUI
[6] https://gist.github.com/cecilemuller/764afa5d4c67e17741d9bc258b45cdc1

Aucun commentaire:

Enregistrer un commentaire