Posts tagged: code

Let’s make this test suite run faster! SoftShake 2010

Qui n’a jamais attendu de nombreuses minutes pour builder un projet ? Et qui n’en a jamais profité pour aller boire un café/regarder twitter/… ? :) Parce que l’on teste fréquemment, il est important d’essayer d’optimiser et de réduire la durée d’exécution des tests.  Tester souvent ne veux pas dire attendre souvent ! Il est donc primordial d’essayer de réduire cette durée en jouant sur différents paramètres. Comment obtenir le résultat des tests le plus rapidement possible ?

Ce qui suit est tiré de la conférence de David Gageot [ @dgageot / http://javabien.net ] à SoftShake 2010.

Sur une vingtaine de personnes présentes dans la salle, les durées de build vont de quelques minutes à plusieurs heures. David présente brièvement quelques frameworks comme Infinitest et JUnitMax. Ce sont des plugins pour IDE Java qui permettent de lancer les tests unitaires en continu et de manière intelligente, c’est à dire uniquement ceux impactés par le code modifié.
La première idée lorsque l’on cherche à optimiser cette durée d’exécution, c’est de vouloir déléguer le problème. C’est faire  tourner les tests sur des serveurs distribués qui permettront d’exécuter les tests en tâches de fond. C’est une mauvaise idée, les serveurs coûtent chers et on peut se retrouver submerger. Il existe des méthodes plus simples pour réduire cette durée.

Le KISS ( Keep It Simple, Stupid ) est également applicable lorsque l’on crée des tests. Chercher à optimises ses tests peut améliorer votre produit : ce qui est simple à tester sera simple à coder et à utiliser. Ce qui est compliqué n’en vaut surement pas la peine. Read more »

Bouchon (stub) ou Simulacre (mock) ?

Le test unitaire est un test qui provoque l’exécution d’une partie du code et qui l’analyse.  Il permet de garantir que le code exécuté fonctionne correctement. Il se doit donc d’être automatisé et répétable. Pour cela, cela nécessite fréquemment le besoin d’isoler le test unitaire des systèmes extérieurs (base de données, appels web-service distants).

Pour ce faire, il existe plusieurs méthodes, l’utilisation de bouchon (‘stub’) ou de simulacre (‘mock’). L’article de référence est un article de Martin Fowler « Les simulacres ne sont pas des bouchons« . Pour résumer, une méthode bouchonnée est appelée sur un objet bouchon réel, centré sur le système testé. Il ne peut jamais faire échouer le test. On regarde l’état de l’objet final à la fin du test et non les étapes qui ont permis d’obtenir cet état. C’est un test basé sur des états. Read more »

Kawa BarCamp 2

J’ai participé hier à la deuxième édition du kawa barcamp. Un bar camp c’est une rencontre ouverte autour de thèmes choisis par les participants. Nous étions hier une trentaine de personnes réunis pendant environ 3 heures. Après un bref tour de table où nous nous sommes présentés (nom + prénom + 3 mots clés), 8 personnes ont inscrits des thèmes d’ateliers sur un tableau. Nous devions choisir parmis 2 sujets au choix : Read more »

Revue de code II

Suite de la revue de code : BigDecimal, nommage et concaténation de String.

Read more »

Revues de code I

Au cours de mes revues de code, je tombe sur des problèmes plus ou moins réguliers.  Outre les problèmes de designs, certains aspects techniques sont récurrents.

Comparaison avec == sur des java.lang.Integer

Le problème, c’est que sur des integers entre -128 et 127, cela peut marcher.  Dans la suite de l’exemple, integer1 et integer2 seront de type java.lang.Integer, int1 sera de type primitif int.

        Integer integer1 = 127;
        Integer integer2 = 127;
        System.out.println(integer1 == integer2); // true

        integer1 = 128;
        integer2 = 128;
        System.out.println(integer1 == integer2); // false

Par contre, on a bien pour toutes les valeurs, y compris celles entre -128 et 127 :

integer1 = new Integer(127);
integer2 = new Integer(127);
System.out.println(integer1 == integer2); // false - normal on a instancié 2 objects différents

integer1 = new Integer(129);
int1 = 129;
System.out.println(integer1 == integer2); // true

Si on regarde la doc de Sun, pour tous les objets entre -128 et 127, l’autoboxing garantit que l’objet renvoyé Integer sera identique pour un même entier primitif. Ce qui ne sera pas le cas au delà de l’intervalle. De plus, rien ne garantit qu’une implémentation étende l’intervalle de cache, raison pour laquelle il est risqué de se baser dessus.
Byte,  Short,  Long ont également le même type de cache. Character a également un cache mais de 0 à 127 (un nombre négatif n’aurait pas eu de sens).

d. Autoboxing is guaranteed to return the same object for integral
values in the range [-128, 127], but an implementation may, at its
discretion, cache values outside of that range. It would be bad style

http://java.sun.com/developer/JDCTechTips/2005/tt0405.html#1

Ainsi, utiliser == en se fiant à l’autoboxing et au cache est une mauvaise pratique, même pour comparer des constantes de faible valeur.

Utiliser plutôt valueOf()  plutôt que new

Il est préferable d’utiliser un valueOf plutôt qu’une nouvelle instanciation à l’aide du constructeur. Valable pour la certains objets immutables.

Exemple avec des types java.lang.Integer, si on regarde la javadoc pour la méthode Integer.valueOf(int i) :

public static Integer valueOf(int i)
Returns a Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values.

Idem pour les Byte, Long, Character, BigInteger … Par contre, préférer quand c’est possible de construire les valeurs accessibles directement via une méthode statique (BigInteger.ONE plutôt que BigInteger.valueOf(1), Boolean.FALSE plutôt que Boolean.valueOf(false) …)

Éviter d’utiliser new String

String test1 = "abc";
String test2 = "abc";
String test3 = new String("abc");
String test4 = new String("abc");

System.out.println(test1 == test2); // true
System.out.println(test1 == test3); //false
System.out.println(test3 == test4); //false

Utiliser new String implique de créer un nouvel object en mémoire. Les String étant immutables, avoir 2 objets différents ayant la même valeur ne fait que consommer de la mémoire. Attention à bien noter le cas 2, « abc » est différent de new String(« abc »).  Cet exemple permet également de montrer l’importance d’utiliser la méthode equals pour comparer les valeurs des objets java.lang.String .

Comme précisé par Jérôme et Nicolas Le Coz en commentaire, l’instanciation d’un nouvel objet String est conseillée lors de l’utilisation de substring sur des très grandes chaînes de caractères. En effet, la valeur stockée avec un substring n’est pas uniquement la substring, mais la chaîne entière avec un offset (premier caractère à utiliser) et un compteur (nombre de caractères à utiliser).

C’est clair si l’on regarde la classe java.lang.String

  public final class String
  	implements java.io.Serializable, Comparable, CharSequence
  {
  /** The value is used for character storage. */
  private final char value[];

  /** The offset is the first index of the storage that is used. */
  private final int offset;

  /** The count is the number of characters in the String. */
  private final int count;
[...]
public String substring(int beginIndex, int endIndex) {
 [...]
 return ((beginIndex == 0) && (endIndex == count)) ? this :
 new String(offset + beginIndex, endIndex - beginIndex, value);
 }

Plus d’infos ici http://blog.xebia.fr/2009/08/03/revue-de-presse-xebia-120/#AttentionvotremmoireavecString et dans les autres liens en commentaires.

Se méfier de la construction des BigDecimal.

On utilise ici 3 manières de faire pour créer un BigDecimal que l’on souhaite égal à 4.12.  Néanmoins, les résultats ne sont pas conformes à ce qu’on pourrait en attendre.

Double myDouble = 4.12D;

BigDecimal bd1 = new BigDecimal(myDouble);
BigDecimal bd2 = new BigDecimal(String.valueOf(myDouble));
BigDecimal bd3 = BigDecimal.valueOf(myDouble);

System.out.println(bd1.equals(bd2));  // false !
System.out.println(bd1.equals(bd3)); // false !
System.out.println(bd2.equals(bd3));  // true

Il n’y a donc pas égalité de valeur entre l’object construit à partir du double et les 2 autres objets. En effet, si on affiche les différentes valeurs des 3 BigDecimal, le problème est facilement visible :

RESULT 1: 4.12000000000000010658141036401502788066864013671875
RESULT 2: 4.12
RESULT 3: 4.12

Il vaut mieux donc éviter d’utiliser le constructeur BigDecimal(double double) et de lui préférer les deux autres méthodes.

public BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double‘s binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.Notes:

  1. The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
  2. The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.
  3. When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.