J'ai besoin de tester une fonction dont le résultat dépendra de l'heure actuelle (en utilisant la fonction isBeforeNow()
de Joda time, cela se produit).
public boolean isAvailable() {
return (this.someDate.isBeforeNow());
}
Est-il possible de bloquer/simuler l'heure du système avec (usin Mockito, par exemple) afin de pouvoir tester la fonction de manière fiable?
Joda time prend en charge la définition d'une heure actuelle "fausse" via les méthodes setCurrentMillisFixed
et setCurrentMillisOffset
de la classe DateTimeUtils
.
Voir https://www.joda.org/joda-time/apidocs/org/joda/time/DateTimeUtils.html
La meilleure façon (IMO) de rendre votre code testable est d'extraire la dépendance de "quelle est l'heure actuelle" dans sa propre interface, avec une implémentation qui utilise l'heure système actuelle (utilisée normalement) et une implémentation qui vous permet de régler l'heure , avancez comme vous le souhaitez, etc.
J'ai utilisé cette approche dans diverses situations, et cela a bien fonctionné. C'est facile à configurer - il suffit de créer une interface (par exemple Clock
) qui a une seule méthode pour vous donner l'instant actuel dans le format que vous voulez (par exemple en utilisant Joda Time, ou éventuellement un Date
).
Java 8 a introduit la classe abstraite Java.time.Clock
qui vous permet d'avoir une implémentation alternative pour les tests. C'est exactement ce que Jon a suggéré dans sa réponse à l'époque.
Pour ajouter à réponse de Jon Skeet , Joda Time contient déjà une interface d'heure actuelle: DateTimeUtils.MillisProvider
Par exemple:
import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils.MillisProvider;
public class Check {
private final MillisProvider millisProvider;
private final DateTime someDate;
public Check(MillisProvider millisProvider, DateTime someDate) {
this.millisProvider = millisProvider;
this.someDate = someDate;
}
public boolean isAvailable() {
long now = millisProvider.getMillis();
return (someDate.isBefore(now));
}
}
Simulez l'heure dans un test unitaire (en utilisant Mockito mais vous pouvez implémenter votre propre classe MillisProviderMock):
DateTime fakeNow = new DateTime(2016, DateTimeConstants.MARCH, 28, 9, 10);
MillisProvider mockMillisProvider = mock(MillisProvider.class);
when(mockMillisProvider.getMillis()).thenReturn(fakeNow.getMillis());
Check check = new Check(mockMillisProvider, someDate);
Utiliser l'heure actuelle en production ( DateTimeUtils.SYSTEM_MILLIS_PROVIDER a été ajouté à Joda Time en 2.9.3):
Check check = new Check(DateTimeUtils.SYSTEM_MILLIS_PROVIDER, someDate);
J'utilise une approche similaire à celle de Jon, mais au lieu de créer une interface spécialisée uniquement pour l'heure actuelle (disons, Clock
), je crée généralement une interface de test spéciale (disons, MockupFactory
). J'ai mis là toutes les méthodes dont j'ai besoin pour tester le code. Par exemple, dans l'un de mes projets, j'ai quatre méthodes:
La classe testée a un constructeur qui accepte cette interface parmi d'autres arguments. Celui sans cet argument crée juste une instance par défaut de cette interface qui fonctionne "dans la vraie vie". L'interface et le constructeur sont tous deux privés du package, de sorte que l'API de test ne fuit pas en dehors du package.
Si j'ai besoin d'objets plus imités, j'ajoute simplement une méthode à cette interface et je l'implémente dans les implémentations de test et réelles.
De cette façon, je conçois un code adapté aux tests en premier lieu sans trop imposer au code lui-même. En fait, le code devient encore plus propre de cette façon, car une grande partie du code d'usine est collectée en un seul endroit. Par exemple, si j'ai besoin de passer à une autre implémentation de client de base de données en code réel, je n'ai qu'à modifier une seule ligne au lieu de rechercher des références au constructeur.
Bien sûr, tout comme dans le cas de l'approche de Jon, cela ne fonctionnera pas avec du code tiers que vous ne pouvez pas ou n'êtes pas autorisé à modifier.