Tests unitaires et multithreading

Comment savoir si une méthode développée fonctionne bien dans un contexte multithread ? Pour répondre à cette question je vous propose cet exemple qui se base sur une classe simple utilisant une instance statique de SimpleDateFormat qui n’est pas un objet threadsafe. Cette classe possède deux méthodes l’une n’est pas threadsafe l’autre l’est grâce à un bloc “synchronisé”.
public class DateUtil { /** Static pour bénéficier d'une instance partagée par l'ensemble des threads.*/ private static DateFormat df = new SimpleDateFormat("dd/MM/yyyy"); public static Date parse(String date) throws ParseException { Date result = null; result = df.parse(date); return result; } public static Date parseThreadSafe(String date) throws ParseException { Date result = null; // Ce bloc garantit que la méthode fonctionne en multithread. synchronized (df) { result = df.parse(date); } return result; } }
La classe de test utilise la librairie TestNg et non JUnit car cette dernière ne propose pas de support du multithread. De son coté, TestNg permet de paramétrer une méthode de test avec, entre autre, deux paramètres :
- invocationCount : le nombre d’invocation de la méthode (valeur par défaut : 1)
- threadPoolSize : la taille du pool de threads qui vont exécuter la méthode (valeur par défaut : 0)
public class DateUtilTest { @Test(threadPoolSize=20, invocationCount=100) public void testParse() { try { for (int i = 10; i < 20; i++) { assertEquals(new SimpleDateFormat("dd/MM/yyyy").parse("01/01/20" + i), DateUtil.parse("01/01/20" + i)); } } catch (ParseException e) { fail("TEST KO : " + e.getMessage()); } } @Test(threadPoolSize=20, invocationCount=100) public void testParseThreadSafe() { try { for (int i = 10; i < 20; i++) { assertEquals(new SimpleDateFormat("dd/MM/yyyy").parse("01/01/20" + i), DateUtil.parseThreadSafe("01/01/20" + i)); } } catch (ParseException e) { fail("TEST KO : " + e.getMessage()); } } }
Les boucles for servent à augmenter la probabilité d’appel simultanés des méthodes testées. Le résultat de l’exécution est assez simple a analyser car les seules méthodes qui tombent en erreur sont celles qui exécutent la méthode sans le bloc synchronisé.
Vous pouvez retrouver le projet maven à l’adresse suivante : https://subversion.assembla.com/svn/everythingiswrong/multithread-tests