Analyse de la qualité du code Java avec JDepend
Intégration
JDepend peut s'intégrer à de nombreux autres outils tels que JUnit, Ant ou encore FitNesse.
Utiliser JDepend avec JUnit
Dans un esprit d'automatisation, les métriques peuvent être collectées automatiquement par JDepend. Comme tout développement logiciel évolue sans cesse, il est possible d'utiliser des test junit lancés lors de chaque compilation pour s'assurer que le programme ne contient pas des dépendances non souhaitées.
On peut ainsi codifier chacune des métriques dans des tests JUnit vérifiant si les valeurs que l'on a définies comme acceptables sont respectées.
Vérification l´existence de dépendances
L'exemple de JUnit suivant teste si une contrainte de dépendance de package est trouvée
import java.io.*;
import java.util.*;
import junit.framework.*;
public class ConstraintTest extends TestCase {
private JDepend jdepend;
public ConstraintTest(String name) {
super(name);
}
protected void setUp() throws IOException {
jdepend = new JDepend();
jdepend.addDirectory("/path/to/project/util/classes");
jdepend.addDirectory("/path/to/project/ejb/classes");
jdepend.addDirectory("/path/to/project/web/classes");
}
/**
* Tests that the package dependency constraint
* is met for the analyzed packages.
*/
public void testMatch() {
DependencyConstraint constraint = new DependencyConstraint();
JavaPackage ejb = constraint.addPackage("com.xyz.ejb");
JavaPackage web = constraint.addPackage("com.xyz.web");
JavaPackage util = constraint.addPackage("com.xyz.util");
ejb.dependsUpon(util);
web.dependsUpon(util);
jdepend.analyze(); assertEquals("Dependency mismatch",
true, jdepend.dependencyMatch(constraint));
}
public static void main(String[] args) {
junit.textui.TestRunner.run(ConstraintTest.class);
}
}
Vérification de l´existence de cycles
L'exemple de JUnit suivant teste l'existence de cycles entre les packages
import java.io.*;
import java.util.*;
import junit.framework.*;
public class CycleTest extends TestCase {
private JDepend jdepend;
public CycleTest(String name) {
super(name);
}
protected void setUp() throws IOException {
jdepend = new JDepend();
jdepend.addDirectory("/path/to/project/ejb/classes");
jdepend.addDirectory("/path/to/project/web/classes");
jdepend.addDirectory("/path/to/project/thirdpartyjars");
}
/**
* Tests that a single package does not contain
* any package dependency cycles.
*/
public void testOnePackage() {
jdepend.analyze();
JavaPackage p = jdepend.getPackage("com.xyz.ejb");
assertEquals("Cycle exists: " + p.getName(), false, p.containsCycle());
}
/**
* Tests that a package dependency cycle does not * exist for any of the analyzed packages.
*/
public void testAllPackages() {
Collection packages = jdepend.analyze();
assertEquals("Cycles exist", false, jdepend.containsCycles());
}
public static void main(String[] args) {
junit.textui.TestRunner.run(CycleTest.class);
}
}
Vérification de valeurs de métriques
L'exemple de JUnit suivant teste que les packages respectent la valeur de la métrique D imposée pour rester en conformité avec les normes de développement fixées pour le projet.
import java.io.*;
import java.util.*;
import junit.framework.*;
public class DistanceTest extends TestCase {
private JDepend jdepend;
public DistanceTest(String name) {
super(name);
}
protected void setUp() throws IOException {
jdepend = new JDepend();
jdepend.addDirectory("/path/to/project/ejb/classes");
jdepend.addDirectory("/path/to/project/web/classes");
jdepend.addDirectory("/path/to/project/thirdpartyjars");
}
/**
* Teste la conformité d'un package par rapport à la condition (tolerance) * imposée à la valeur de la "Distance from the main sequence" ( métrique D)
*/
public void testOnePackage() {
double ideal = 0.0;
double tolerance = 0.125; // project-dependent
jdepend.analyze();
JavaPackage p = jdepend.getPackage("com.xyz.ejb");
assertEquals("Distance exceeded: " + p.getName(), ideal, p.distance(), tolerance);
}
/**
* Teste la conformité de tous les packages analysés par rapport à
* la condition (tolerance) imposée à la valeur de la "Distance from the main sequence" ( métrique D)
*/
public void testAllPackages() {
double ideal = 0.0;
double tolerance = 0.5; // project-dependent
Collection packages = jdepend.analyze();
Iterator iter = packages.iterator();
while (iter.hasNext()) {
JavaPackage p = (JavaPackage)iter.next();
assertEquals("Distance exceeded: " + p.getName(), ideal, p.distance(), tolerance);
}
}
public static void main(String[] args) {
junit.textui.TestRunner.run(DistanceTest.class);
}
}
Utiliser JDepend avec Ant
On peut inclure une t‰che ant dans le build.xml pour la génération automatique de l´analyse de la qualité du code
Génération de rapport dans un fichier texte
L'exemple de tâche ant suivant lance JDepend dans le "build directory" et place le rapport dans le répertoire docs/jdepend-report.txt
<target name="jdepend">
<jdepend outputfile="docs/jdepend-report.txt">
<exclude name="java.*"/>
<exclude name="javax.*"/>
<classespath>
<pathelement location="build" />
</classespath>
<classpath location="build" />
</jdepend>
</target>
Génération de rapport dans un fichier XML et HTML
A partir de sa version 1.5, ant inclut un attribut format pour la tâche ant JDepend et une feuille de style XSL par défaut permettant de générer un rapport au format HTML. L'exemple de tâche ant suivant lance JDepend dans le "build directory" et place le rapport XML dans le répertoire docs/jdepend-report.xml et génère le fichier jdepend.html dans le répertoire etc
<target name="jdepend">
<jdepend format="xml" outputfile="docs/jdepend-report.xml">
<exclude name="java.*"/>
<exclude name="javax.*"/>
<classespath>
<pathelement location="classes" />
</classespath>
<classpath location="classes" />
</jdepend>
<style basedir="docs" destdir="docs"
includes="jdepend-report.xml" style="${ant.home}/etc/jdepend.xsl" />
</target>