Testabilité des IHM : commençons (déjà) par Swing!

le 02/04/2010 par Olivier Mallassi
Tags: Software Engineering

Tester l'IHM n'a jamais été chose aisée et globalement deux approches s'opposent : - Tester avec du code. Le principal inconvénient est que cela repose principalement sur le nommage ou l'agencement des composants et - suivant le framework utilisé - peut être assez sensible au refactoring et notamment au modification d'imbrication des composants. - Tester en mode recorder. Le principal inconvénient reste que ces tests ne peuvent être réalisés que très tardivement (et souvent pas par les équipes de développements) et sont sensibles aux modifications d'IHM (repositionnement d'un bouton etc...). Tout refactoring de ce type de tests est impossible et ces outils sont souvent croisés dans des contextes de pure non regression - ie. des contextes où le développement IHM est stable et où l'on souhaite facilement savoir si quelque chose a été cassée ou non.

Il existe quelques solutions de tests d'IHM pour Swing (Swing Unit, SpecUI4j...) mais j'ai envie de m'arrêter sur Fest Swing. Prenez le code Swing suivant :

...
lineNumberTextField.setName(LINE_NUMBER_TEXT_FIELD);
...
searchCriteria1Panel.add(lineNumberTextField, cc.xywh(12, 2, 3, 1));
...
queryButton.setName(QUERY_BUTTON);

//---- queryButton ----
queryButton.setText(bundle.getString("search.queryButton.text"));
queryButton.setAction(queryAction);
searchActionPanel.add(queryButton, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
...

Un panel propose des champs de recherche, un bouton permettant de déclencher cette recherche. Bref, de l'hyper classique. Le code de tests (développé avec Fest) est le suivant :

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext-test.xml" })
public abstract class MyTest extends FestSwingTestCaseTemplate {
...set up must be done. see Fest documentation for more details
    protected FrameFixture editor;
    private JTabbedPaneFixture criteriaPane;
    private JButtonFixture queryButton;

@Test
public void testWhatYouWannaTest() {
    queryButton = editor.button(QUERY_BUTTON);
    editor.textBox(LINE_NUMBER_TEXT_FIELD).enterText("123456");
...
    analysisModel.getSelectedCauseModel().setSelectionIndex(2);
    Assert.assertEquals("libCause1", editor.textBox(CAUSE_TEXT_FIELD).text());

    criteriaPane.selectTab(1);
    editor.textBox(BRANCH_TEXT_FIELD).enterText("");
    editor.comboBox(ENTRY_DATE_OPERATOR_COMBO).selectItem(1);

    queryButton.click();
    // asserts
    final SearchCriteria searchCriteria = systemUnderTest.getSearchCriteria();
    Assert.assertEquals((Long) Long.parseLong("123456"), searchCriteria.getLineNumber());
    Assert.assertNull(searchCriteria.getBranch());
    Assert.assertEquals(10, searchCriteria.getTop());
...

Simple, efficace, lisible.

Ainsi Fest Swing présente les avantages suivants : - Exécution sous la forme de tests JUnit. L'exécution des tests reste assez rapide. - Intégration à l'automate de build. Puisque définis au format jUnit, ces tests sont facilement intégrables au build continu (via éventuellement un profil particulier). - API (voire un DSL) limpide à utiliser. Le code des tests est expressif et il est possible de récupérer des boutons, de déclencher des actions, de saisir du texte... - Récursivité du "moteur" de sélection de composants. Tous les frameworks ne le propose pas (UISpec4J par exemple) mais c'est pourtant essentiel tant en termes de productivité des développements qu'en termes de stabilité des tests. Sur l'aspect productivité, le développement d'IHM implique d'empiler les composants et les panels (JComponents, etc...). Fest retrouve de façon récursive, sur la base d'un nom logique, n'importe lequel des composants dans l'arborescence de composants (le fameux tableaux de components pour les techies). Au niveau de la stabilité des tests, vous pouvez donc modifier et refactorer le code Swing (empilement des composants...) sans introduire de fausses régressions.

Mais au delà de l'outil (qui reste essentiel), reste une question : que veut-on tester? De quels défauts souhaite-t-on se prémunir? De mon expérience, il peut être intéressant de tester du comportement (déclenchement d'actions...), du technique (binding dans le cas d'utilisation d'outils type jGoodies, habilitation sur la fonction et la donnée...) ou encore des scenarii métiers complets (saisie des paramêtres de recherche, remplissage de la liste de résultat, affichage du nombre de résultat dans le label prévu à cet effet...). Sur ces aspects Fest Swing fonctionne. En revanche, tester les navigations entre écrans, la gestion du fenêtrage, des menus peut être plus complexes.

Et vous, au niveau de l'IHM que testez vous? Avec quels outils?