JJUnit est un système de construction de "tests unitaires" du logiciel. Les tests unitaires sont de petits tests qui vous assurent que les parties individuelles du logiciel font ce qu'elles sont censées faire. Dans un projet distribué comme JMRI, où il y a beaucoup de développeurs qui peuvent perdre la communication avec les autres, les tests unitaires sont une bonne façon pour s'assurer que le code n'a pas été cassé par un changement.
Pour plus d'informations sur JUnit, voir la page d'accueil JUnit. Un exemple très intéressant du développement basé sur les tests est disponible à partir du livre de Robert Martin .
Certaines classes ont des tests JUnit disponibles. C'est bon d'ajouter des tests JUnit quand vous apportez des modifications, tester vos nouvelles fonctionnalités pour s'assurer qu'elle est au travail, et continue à travailler), lorsque vous devez comprendre le code que quelqu'un a fait (les tests documentent exactement ce qui doit arriver!), et quand vous traquer un bug (assurez-vous qu'il ne revient pas).
ant antalltest
Cela compilera le code de test, qui est dans le sous-répertoire "test"
du répertoire "java" dans nos distributions habituelles de code,
et puis exécutez les tests sous une interface graphique.
(Pour vous assurer que vous avez tout recompilé, vous
voudrez peut-être faire ant clean
en premier).
Si vous connaissez le nom
de votre classe de test, ou la classe de test pour votre paquet, vous
pouvez exécuter directement avec le script "runTest" :
ant tests
./runtest.cshjmri.jmrit.powerpanel.PowerPanelTest
Le première ligne compile tout le code de test, et la seconde
effectue un test spécifique ou une suite de test.
Vous pouvez visiter le site web pour vous inscrire sur la liste e-mail de jmri-buildpour obtenir les mauvaises nouvelles aussi rapidement que possible, ou surveiller pour voir les archives de la liste e-mail et voir les journaux du passé. Ou vous pouvez surveiller le "tableau de bord" sur le site web intégration continue.
( Quand la construction réussit, rien n'est envoyé, pour pour réduire le trafic )
Il y a des classes supplémentaires qui sont utilisées pour grouper des classes de test pour un paquet particulier dans des suites de test JUnit.
Pour cette classe test, ajoutez une ou plusieurs méthodes de test utilisant les conventions JUnit. Basiquement, chaque méthode nécessite un nom qui démarre avec "test", exemple: "testFirst" et doit avoir une signature "publique vide". JUnit gèrera tout ce qui suit. En général, les méthodes de test doivent être petites, testant juste une partie de l'opération classe. C'est pourquoi elles sont appelées tests "unitaire".
L'usage de Log4j dans les classes de test elle-mêmes a deux aspects:
Pour permettre ceci, JMRI fonctionne et il utilise des tests avec un dispositif log4j spécial, qui stocke les messages de sorte que les tests JUnit peuvent les regarder avant de les transmettre dans le journal. Il y a deux aspects pour faire ce travail:
@Before
public void setUp() throws Exception {
jmri.util.JUnitUtil.setUp();
}
@After
public void tearDown() throws Exception {
jmri.util.JUnitUtil.tearDown();
}
log.warn("Provoked message");
la case du test appelant devrait suivre avec la ligne:
jmri.util.JUnitAppender.assertWarnMessage("Provoked message");
jmri.util.JUnitUtil.setUp();
de sorte qu'elles peuvent être exécutées de façon indépendante.
Note: Nos CI test exécutables sont configurés pour échouer si des messages FATAL ou ERROR sont émis au lieu d'être traités. Cela signifie que même si vous pouvez exécuter vos tests avec succès sur votre ordinateur, s'ils emettent des messages d'erreurs, mais vous ne serez pas en mesure de fusionner votre code dans le répertoire commun jusqu'à ce que ceux-ci soient traités.
Dépendant du code utilisé par vos gestionnaires, votre mise en œuvre de configuration()
devrait commencer par:
jmri.util.JUnitUtil.setUp();
( Vous pouvez omettre l'initialisation des gestionnaires dont vous n'avez pas besoin )
Voir la classe jmri.util.JUnitUtil pour la liste complète de ceux qui sont disponibles, et svp ajoutez
en plus si vous avez besoin d'un que vous n'avez pas déjà.
jmri.util.JUnitUtil.resetInstanceManager();
jmri.util.JUnitUtil.initInternalTurnoutManager();
jmri.util.JUnitUtil.initInternalLightManager();
jmri.util.JUnitUtil.initInternalSensorManager();
Votre désassemblage()
doit se terminer par
jmri.util.JUnitUtil.resetInstanceManager();
jmri.util.JUnitUtil.tearDown();
Si vous voulez attendre pour une condition spécifique pour être vrai, ex: recevoir une réponse d'un objet, vous pouvez utiliser un appel à la méthode waitFor qui ressemble à:
JUnitUtil.waitFor(()->{reply!=null}, "reply didn't arrive");
Le premier argument est une une fermeture lambda, un petit morceau de code
qui est évaluer répétitivement jusqu'à être vrai. La chaîne, deuxième argument, est le
texte de l'assertion ( message d'erreur ) que vous obtiendrez si la condition
ne devient pas vrai dans un temps raisonnable.
L'attente d'un résultat spécifique est plus rapide et plus fiable. Si vous ne pouvez pas faire cela pour une raison quelconque, vous pouvez faire une attente basée sur un temps court
JUnitUtil.releaseThread(this);
Celui-ci utilise un retard nominal.
Notez que celui-ci ne devrait pas être utilisé en synchronisme avec les tâches Swing Voir le Test Code Swing, une section faite pour ça.
En général,vous ne devriez pas avoir d'appels pour dormir(), attendre(), produire() dans votre code. Utilisez l'aide de JUnitUtil pour ceux en place.
Certains tests devront commencer les tâches, par exemple pour tester pour tester les commandes de signaux ou les aspects sur le réseau I/O
Principes généraux: vos tests doivent obéir à un fonctionnement fiable:
Si vous faites de multiples tests avec les tâches, vous devez
attendre la tâche pour réellement arrêter avant de passer à l'opération suivante.
Vous pouvez faire cela avec un appel JUnitUtil.waitFor(..)
qui attends certains drapeau dans la tâche.
code()
qui doivent se produire avant de tester son fonctionnement,vous devez également attendre pour ceux à terminer.
Par exemple, si la création d'une tâche est basée sur AbstractAutomat vous pouvez vérifier le démarrage avec:
AbsractAutomat p = new MyThreadClass();
p.start();
JUnitUtil.waitFor(()->{return p.isRunning();}, "logic running");
et s'assurer de la fin avec:
p.stop();
JUnitUtil.waitFor(()->{return !p.isRunning();}, "logic stopped");
Voici quelques idées qui peuvent aider à éviter ces types de problèmes.
String tempDirectoryName = "temp";
if ( ! (new File(tempDirectoryName).isDirectory())) {
// create the temp directory if it does not exist
FileUtil.createDirectory(tempDirectoryName);
}
String filename = tempDirectoryName + File.separator + "testcaseFile.txt";
String filename = tempDirectoryName + File.separator + "testcaseFile.txt";
File file = new File(filename);
if (file.exists()) {
try {
file.delete();
} catch (java.lang.Exception e) {
Assert.fail("Exception while trying to delete the existing file " +
filename +
".\n Exception reported: " +
e.toString());
// perform some appropriate action in this case
}
}
if (log.isDebugEnabled()) {
log.debug("Path to written hex file is: "+filename);
}
else {
file.delete();
}
java.io.File.createTempFile("testcasefile","txt")
fonctionneront de manière fiable dans l'environnement
construire en intégration continue.Les questions ci-dessus ont été identifiées par l'intermédiaire d'une testcase qui a exécutée correctement sur un PC sous Windows pour les deux cibles ant "alltest" et "headlesstest", peu importe combien de fois il a été exécuté. dans l'environnement intégration continue le test a couru correctement la première fois après avoir été vérifié, mais a échoué pour chaque exécution de l'environnement d'intégration continue ultérieur de "headlesstest".Une fois que le test a été modifié sur la base des recommandations de fichiers temporaires présentées ici, le test est devenu stable sur plusieurs exécutions d'intégration continue de "headlesstest".
show ()
ou setVisible (true)
), il ne peut pas être accessible de manière
fiable à partir de la tâche JUnit. Même l'utilisation de la technique de
retard d'écouteur décrit ci-dessus n'est pas fiables.
Parce que nous utilisons les tests en mode "headless" pendant les constructions en intégration continue il est important que l'accès de Swing ( et AWT ) à des tests soit enfermé dans un mode de vérification:
if (!System.getProperty("jmri.headlesstest","false").equals("true")) {
suite.addTest(myTest.suite());
}
Ceci exécute Le myTest suite de test seulement quand un affichage est disponible.
Les test GUI* doivent fermer les fenêtres quand ils sont terminés, et en général se nettoient après eux-mêmes. Si vous voulez garder les fenêtres autour de sorte que vous pouvez les manipuler, par exemple pour le test manuel ou débogage vous pouvez utiliser le paramètre système jmri.demo pour contrôler que:
if (!System.getProperty("jmri.demo", "false").equals("false")) {
myFrame.setVisible(false);
myFrame.dispose();
}
Pour de nombreux tests, vous pourrez à la fois faire des tests fiables et améliorer la structure de votre code en séparant le code GUI (Swing) de la logique JMRI et des communications. Cela vous permet de vérifier le code logique séparément, mais en invoquant ces méthodes et de vérifier l'état de leurs mise à jour.
Pour des tests GUI* plus compliqués, nous utilisons JFCUnit pour contrôler les interactions avec les objets Swing.
Pour un très simple exemple de l'utilisation de JFCUnit, voir le fichier test/jmri/util/SwingTestCaseTest.java.
Pour utiliser JFCUnit,vous héritez premièrement de votre classe depuis
SwingTestCase
au lieu de TestCase
.
Cela est suffisant pour obtenir le fonctionnement de base des tests Swing; la classe de base
interrompt la tâche de test jusqu'à ce que Swing (en fait, le mécanisme d'événement AWT)
a terminé tous les traitements après chaque appel Swing dans le test.
Pour cette raison, les tests vont s'exécuter beaucoup plus lentement si vous vous déplacez par exemple
le curseur de la souris pendant qu'ils s'exécutent)
Pour les tests plus complexe de l'interface graphique, vous pouvez appeler les différents aspects de l'interface et vérifier l'état interne en utilisant le code de test.
jython
et appelés par
java/test/jmri/jmrit/jython/SampleScriptTest.java
.
Voir jmri_bindings_test
.py
exemple pour la syntaxe.
La classe SampleScriptTest
est un espace réservé, et doit (éventuellement) être étendu pour ramasser automatiquement les fichiers,
pour soutenir les essais Unit, etc.
Comme un test seulement , vous pouvez essayer de régler l'option "-noloading" dans le
main
de n'importe quel test de classe avec lequelque vous avez des problèmes avec:
static public void main(String[] args) {
String[] testCaseName = {"-noloading", LogixTableActionTest.class.getName()};
junit.swingui.TestRunner.main(testCaseName);
}
SVP ne laissez pas "-noloading" en place, car il empêche les gens de réexécuter le test
dynamique. Au lieu de cela, le bon correctif à long terme est d'avoir toutes les classes
ayant des problèmes chargeur JUnit incluses dans le fichier
test/junit/runner/excluded.properties
JUnit utilise ces propriétés pour décider comment gérer le chargement et le rechargement des classes.
GUI* = Interface Graphique Utilisateur