Les tests unitaires en Java: Given, When, Then (ou Arrange, Act, Assert suivant l’idéologie)

Un test unitaire doit être découpé en trois parties.

L’instanciation (Given ou Arrange): Cette partie décrit l’état au début du test. C’est la définition du scénario.

L’éxécution (When ou Act): Le comportement à vérifier. Un seul comportement à vérifier par test !!!

La vérification (When ou Assert): La description de ce que l’on attend en sortie du test.

Ci dessous, un exemple définit par Martin Fowler:

Si le test ne rentre pas dans cette structure, c’est qu’il doit être refactoré.

Voici un exemple de ce que ça donne en Java:

Les tests unitaires en Java: Instanciation d’objets en série

Lorsque nous créons des tests unitaires à la chaîne, un des soucis qui se pose rapidement est l’instanciation répétée d’objets souvent similaires pour ne pas dire identiques. On se retrouve vite avec des copier coller en série, qui, au premier refactoring de code, se transforme en corrections en série.

Cette approche basique n’est pas viable pour maintenir une série de tests sur un projet de grande envergure. Un changement sur une classe business object ne doit avoir qu’un impact limité sur les tests qui l’utilisent.

Les tests unitaires sont là pour être efficaces sur le long terme et pour nous faire gagner du temps.

Le pattern Test Data Builder

Il s’agit d’une factory qui permet d’instancier des objets business. Prenons l’exemple d’un projet pour une banque qui servirait à gérer un portefeuille de clients. La classe business object du client serait comme suit:

La création de cet objet et de tous ces sous objet peut être très fastidieux. Pour cela, le pattern Test Data Builder va nous permettre de faciliter la création des tests mais aussi la maintenance évolutive. Voir l’exemple d’utilisation ci-dessous:

L’instanciation apparaît maintenant très clairement, contrairement à ce que pourrait produire une série de setters avec des instanciations à la chaîne. Il est aussi beaucoup plus facile de suivre les différents cas de tests On sait de suite quel cas de test plante et quel est le sujet exact qui pose un soucis. Et en plus, les Test Data Builders améliorent la maintenabilité des tests en séparant bien les données de la logique. La création des objets métiers est encapsulée, et ne vient pas polluer le code des tests.

Le builder est comme suit:

On peut ainsi créer des cas réutilisables à souhait, pour couvrir au maximum les cas de tests du projet. Le product owner peut ainsi décrire les clients typiques et il est aisé de les recréer dans le builder. Il est notable que j’ai utilisé des appels statiques pour créer les sous objets du client. Ce choix va dépendre des besoins et contraintes spécifiques à chaque projet et structure de données.

Concernant les champs propres des objets instanciés, il est judicieux de choisir des valeurs limites. Ainsi, si le champs client_name en base est sur 256 caractères, l’initialisation dans le builder pourra être faite avec un une chaîne aléatoire de 256 caractères.

Faire dialoguer des objets avec MQTT

L’Internet des objets

Le sujet d’aujourd’hui est bien à la mode. Il y a beaucoup de hype autour de ce terme. Mais qu’est ce que c’est réellement ? Prenons la définition de whatis :

The Internet of Things (IoT) is a scenario in which objects, animals or people are provided with unique identifiers and the ability to transfer data over a network without requiring human-to-human or human-to-computer interaction. IoT has evolved from the convergence of wireless technologies, micro-electromechanical systems (MEMS) and the Internet.

D’après cette définition, l’IoT (Internet of things) est la possibilité d’avoir sa machine à laver branché au net. Idéal pour recevoir sur son smartphone un message pour indiquer que les caleçons sont propres. Cool.

En vrai, qui l’utilise ?

Bien entendu, il y a des utilisations plus sérieuse. La voiture qui contacte automatiquement les secours en cas d’accident. Le controle de la température et de l’humidité dans une serre. Le suivi des animaux dans une ferme. Etc.

Tous ces modules sont différents d’un point de vue du design, du nombre et du genre des capteurs, poids, taille, etc. Chaque module a ses propres besoins mais une contrainte revient assez souvent. Ils sont disséminé à travers le paysage et doivent fonctionner dans des conditions souvent difficiles. L’autonomie, la qualité de la réception, ou la quantité de données à transférer sont les principales difficultés quand on construit ce genre de module.

Pourquoi MQTT ?

Prenons le plus petit message envoyé par HTTP.

  • Header TCP; 20 bytes
  • Header IP: 20 bytes
  • Data: 1 byte

On se retrouve à envoyer au minimum 41 bytes pour un message. Prenons un module connecté par GSM avec une carte SIM limitant le traffic à 5Mo par mois (cas réél rencontré récemment), le nombre de message transféré sera très limité.

Prenons maintenant le même message envoyé par MQTT.

  • Message: 2 bytes

Desuite, il y a beaucoup moins de limitations. Prenons ensuite en compte la consomation avec le pourcentage de la batterie utilisée pour envoyer un message durant une expérience (source) ;

  • 0.01709% pour HTTP
  • 0.00010% pour MQTT

Là encore,  on voit bien l’intérêt de limiter le transfert de données.

 Comment ça marche ?

Le protocole en lui-même est assez simple. Connect, subscribe, publish. On publit des messages sur un topic dans un broker. D’autres personnes écoutent ce qui est publié et reçoivent le message.

Il y a plusieurs implémentations de MQTT, dans à peu près tous les langages (Ok, peut être pas en brainfuck). Je vais me baser sur PAHO pour Java.

	
<dependency>
  <groupId>org.eclipse.paho</groupId>
  <artifactId>mqtt-client</artifactId>
  <version>0.2.1</version>
</dependency>

Voilà l’implémentation la plus basique.

	private MqttClient mqttClient;
	
	public void connectToBroker(String clientId) throws Exception {
		final MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
		mqttClient = new MqttClient("tcp://iot.eclipse.org:1883", clientId);
		mqttClient.setCallback(this);
		mqttClient.connect(mqttConnectOptions);
	}
	
	public void subscribe(String topic) throws Exception {
		mqttClient.subscribe(topic, 0);
	}
	
	public void publish(String topic, String message) throws Exception {
		final MqttMessage mqttMessage = new MqttMessage(message.getBytes());
		MqttDeliveryToken mqttDeliveryToken = mqttClient.getTopic(topic).publish(mqttMessage);
		mqttDeliveryToken.waitForCompletion();
		Thread.sleep(100);
	}
	
	public void connectionLost(Throwable arg0) {
	}

	public void deliveryComplete(IMqttDeliveryToken arg0) {
	}

	public void messageArrived(String arg0, MqttMessage message) throws Exception {
		System.out.println(new String(message.getPayload()));
	}
	
	public static void main(String[] args) throws Exception {
		FullMqttPoc poc = new FullMqttPoc();
		poc.connectToBroker("publisher");
		poc.subscribe("topicPocMqtt");
		poc.publish("topicPocMqtt", "ceci est mon message");
	}

Tout d’abord, le broker. Pour cet exemple, j’ai utilisé le broker en libre accès fourni par la fondation Eclipse à l’adresse tcp://iot.eclipse.org:1883. Une large palette de broker est disponible pour de vrais projets comme Mosquitto ou HiveMQ. La connection se fait sans identifiants, mais une version sécurisé est disponible également. Pour notre exemple, seul un clientId est nécessaire, celui-ci devant être unique sur le broker.

La suite se fait par publish/subscribe. Dans notre exemple, un seul processus joue les deux rôles. La console affichera dont juste “ceci est mon message”. Un exemple plus complet est disponible ici avec deux processus distincts.

Design patterns: Le singleton

Le singleton est un design pattern (ou patron de conception si vous êtes franchouillard) qui permet de limiter l’instanciation d’une classe à un seul objet dans un système. C’est le design pattern le plus (mal) utilisé par les développeurs JAVA. Le principe de base du singleton est de bloquer l’accès au constructeur (en le mettant privé) et d’avoir une méthode qui retourne l’instance après l’avoir créée si elle n’existe pas déjà.

Le singleton représente des risques quand il est mal utilisé. En milieu multi-threadé, l’accès à l’instance peut causer des pertes de performance en cas de système synchronisé. Egalement, son utilisation à tort et à travers dans des classes XxxxxManager ou XxxxxUtils donne à certains des envies de mettre des baffes. La bonne pratique pour la création de ce genre de classe est toute simple: Ne le faites pas.

Lazy initialisation

Une méthode (trop) couramment utilisée pour créer le singleton. La création de l’instance est retardée jusqu’au moment où on en a rééllement besoin. Néanmoins, la méthode est synchronized. Il en résulte donc de piétres performances en environnement multithreadé. Si l’instance a besoin d’être accédée régulièrement, l’application pourra montrer d’importants ralentissements.

public class LazyInitialization {
	private static LazyInitialization instance = null;

	private LazyInitialization() {}

	public static synchronized LazyInitialization getInstance() {
		if (instance == null) {
			instance = new LazyInitialization();
		}
		return instance;
	}
}

Double checked locking

Une manière de limiter le problème du synchronized est de limiter la synchronisation au moment de la création. Néanmoins, celà ne fonctionne pas dans un environnement multithread. Il s’agit ici d’un bel exemple de “code juste” qui ignore ses fautes. Autrefois populaire, ce type de singleton est aujourd’hui considéré comme un antipattern.

public class DoubleCheckedLocking {
	private static volatile DoubleCheckedLocking instance = null;

	private DoubleCheckedLocking() { }

	public static DoubleCheckedLocking getInstance() {
		if (instance == null) {
			synchronized (DoubleCheckedLocking.class) {
				if (instance == null) {
					instance = new DoubleCheckedLocking();
				}
			}
		}
		return instance;
	}
}

Eager initialisation

Si le systéme doit utiliser une instance de manière très régulière, il est possible d’utiliser cette méthode. Il n’y a pas de synchronized, ce qui améliore les performances, et l’instance n’est créée que lorsque la classe est utilisée. L’instance est créée au chargement ce qui implique la présence en mémoire d’objet pas forcément utilisés et il n’y a pas de gestion des exceptions.

public class EagerInitialization {
	private static final EagerInitialization INSTANCE = new EagerInitialization();

	private EagerInitialization() { }

	public static EagerInitialization getInstance() {
		return INSTANCE;
	}
}

Static initialisation

Une variante est l’initialisation statique. La création dans un bloc statique permet la gestion des exceptions. Mais là encore, il y a en mémoire des objets qui ne sont pas encore utilisés.

public class StaticInitialization {
	private static final StaticInitialization instance;

	static {
		try {
			instance = new StaticInitialization();
		} catch (Exception e) {
			throw new RuntimeException("Error on singleton initialization", e);
		}
	}

	public static StaticInitialization getInstance() {
		return instance;
	}

	private StaticInitialization() {
	}
}

On demand holder

Cette version a été développée suivant les recherches de William Pugh de l’université du Maryland. Cette méthode fonctionne dans toutes les versions de Java et retarde au maximum l’initialisation et est totalement thread-safe sans nécessiter l’utilisation de volatile ou de synchronized. Il s’agit du pattern le plus populaire actuellement. La présence de la inner class permet de retarder la création de l’instance au moment de l’appel de la méthode getInstance().

public class OnDemandHolder {

	private OnDemandHolder() {
	}

	private static class SingletonHolder {
		private static final OnDemandHolder INSTANCE = new OnDemandHolder();
	}

	public static OnDemandHolder getInstance() {
		return SingletonHolder.INSTANCE;
	}
}

Enum

L’auteur d’Effective JavaJoshua Bloch a presenté une solution basée sur enums. D’après lui, “a single-element enum type is the best way to implement a singleton”. Cette version ne fonctionne qu’à partir de Java 1.5. Le fait que ce soit un enum nous assure que la valeur est instantiée une seule fois dans le programme et est accessible globalement. Néanmoins, il n’y a pas de possibilité de lazy initialisation avec cette technique.

public enum Singleton {
    INSTANCE;
    public void execute (String arg) {
        // [...] 
    }
}

source: github

Introduction

Bonjours à tous et bienvenu sur mon blog dédié au code.

Je suis un développeur, passioné par mon métier, avec près de 10 ans d’expérience dans des domaines variés et sur toutes sortes de technologies. Surtout les pires.

Vous ne serez peut être pas d’accord avec moi. Parfois ou souvent. Ne vous génez pas pour le dire. Et le cas échéant, j’en tiendrai peut être compte. Ou pas.

Pour finir cette introduction, je voudrais vous citer mon professeur d’Ada95 et d’algorithmique, Daniel Feneuille:

Du code juste, c’est du code faux qui s’ignore