L'autre jour, j'ai travaillé sur la mise à jour de la version de JMockit utilisée dans le test unitaire (1.13 → 1.46). Notez les modifications apportées à l'ancien et au nouveau JMockit et comment les corriger lors de la mise à niveau.
Nous espérons qu'il sera utile aux deux types de personnes suivants.
JMockit est une bibliothèque fictive pour Java. Vous pouvez remplacer (simuler) le comportement des méthodes existantes dans le test unitaire.
Page officielle de JMockit → La boîte à outils de test JMockit
Les remplacements sont définis à l'aide de l'initialiseur d'instance et des variables d'instance spéciales "result" et "times".
new Expectations() {{
obj.method(arg1, arg2, arg3);
result = value;
times = 2;
}};
Il prend également en charge des opérations assez puissantes et peut être remplacé comme suit (voir la page officielle pour plus de détails).
--Remplacement de la méthode statique
--Remplacer uniquement les méthodes spécifiques d'objets (moquage partiel)
--Remplacement des objets retournés par new
Une version mineure sera publiée tous les quelques mois. Vous pouvez consulter les versions précédentes de cette page:
Comme vous pouvez le voir dans l'historique, JMockit est une politique qui ** tronque de plus en plus les anciennes fonctionnalités **. Par exemple, la méthode «Deencapsulation.setField» est obsolète depuis 2 mois et 2 versions.
Version 1.45 (Jan 27, 2019): Removed the Deencapsulation.setField methods, which were deprecated in version 1.44.
Version 1.44 (Nov 25, 2018): Deprecated the Deencapsulation.setField methods, in favor of @Tested and @Injectable.
Par conséquent, ** si vous essayez de mettre à jour la version en une seule fois, de nombreux changements seront nécessaires et le travail sera difficile **.
Nous recommandons non plus.
Les rivaux de JMockit incluent Mockito. Mockito a plus d'utilisateurs et une notation plus naturelle (surtout si vous êtes habitué au RSpec de Ruby).
De plus, Mockito seul ne peut remplacer que les méthodes normales, mais lorsqu'il est combiné avec Powermock, il sera possible d'effectuer les mêmes changements "sales" puissants que JMockit. Je vais.
//Image de la notation mockito
when(obj.method(arg1, arg2, arg3)).thenReturn(value);
Par conséquent, il est recommandé de passer de ** J Mockit à Mockito ** si possible.
Dans ce produit, j'ai abandonné la migration vers Mockito car il y avait beaucoup de tests utilisant JMockit.
Vous pouvez mettre à niveau par les opérations suivantes.
Cependant, comme expliqué dans "Ecrire à éviter dans la dernière version", il y a des cas où la compilation réussit mais elle ne fonctionne pas comme prévu. Si le test échoue, suspectez-le.
De plus, il n'est pas recommandé de passer de l'ancienne version à la dernière version car il y aura trop de changements et cela tombera en panne.
Aussi,
Je ne pense pas qu'il existe un chemin de raccourci comme celui-là. ** Nous vous recommandons de mettre à jour régulièrement une version à la fois **.
Lisez la ** dernière version du didacticiel ** avant de mettre à niveau ou d'écrire des tests.
JMockit est une bibliothèque avec une forte habitude en premier lieu. Ça fait mal quand je pense: "C'est la même chose que Mockito, seul le style d'écriture est différent." Par exemple, si vous obtenez un objet fictif pour une classe qui est * @ Mocked
, toutes les instances de cette classe seront simulées *.
De plus, comme mentionné ci-dessus, les fonctions ont été audacieusement révisées et supprimées et le style d'écriture a considérablement changé. JMockit expérimenté devrait également lire le code avant de le toucher.
Avez-vous lu la dernière version du didacticiel?
OK!
Je vais vous expliquer les changements de la version précédente qui sont susceptibles d'avoir un impact important (je suis désolé pour les fonctions détaillées, mais veuillez vérifier les notes de publication, etc.).
Auparavant, JMockit était chargé lorsque la classe de test JUnit était annotée avec @ RunWith (JMockit.class)
.
Maintenant, la méthode a changé, et lors de l'exécution d'un test unitaire, si vous spécifiez le fichier jar JMockit comme javaagent comme option VM comme indiqué ci-dessous, JMockit sera chargé.
-javaagent:/path/to/jmockit-1.46.jar
Pour IntelliJ, définissez l'emplacement suivant.
"Nom du module à côté du bouton Exécuter dans la barre d'outils" → Modifier les configurations → Modèles → JUnit → Configurations → Options VM
@ RunWith (JMockit.class)
AnnotationComme mentionné ci-dessus, il est obsolète, supprimez-le donc.
retourne
Pour le paramètre à un seul argument «renvoie», utilisez «result =« à la place. Vous pouvez continuer à utiliser des «retours» avec au moins deux arguments.
Il est difficile de réécrire les retours
un par un, mais j'ai écrit à ce sujet dans un autre article, alors veuillez vous y référer (→ Comment ai-je remplacé en toute sécurité les retours (Object) dans JMockit?" ).
// Old
new Expectations() {{
obj.method(arg1, arg2, arg3); returns(value);
}};
// New
new Expectations() {{
obj.method(arg1, arg2, arg3); result = value;
}};
NonStrictExpectations
NonStrictExpectations
était la" version de ʻExpectations` qui ne cause pas d'erreur même si la méthode remplacée n'est pas appelée ".
Remplacez-le par ʻAttentescomme indiqué ci-dessous, et définissez le nombre minimum d'appels à 0 avec
minTimes = 0`.
new Expectations() {{
mock.get(0); result = "1"; minTimes = 0;
}};
De plus, "j'ai remplacé la méthode, mais elle ne s'appelle pas" est
Dans de nombreux cas, veuillez consulter le scénario de test.
StrictExpectations
Réécrivez-le simplement en tant qu'attentes.
Deencapsulation
La désencapsulation était la possibilité de parcourir et de modifier les champs privés.
package-private
ou public
Relâchez les restrictions d'accès pour les champs que vous avez modifiés avec Désencapsulation
, et autorisez leur accès avec .
à partir du test. Annotez le champ avec @ VisibleForTesting
de Guava.
Vous apporterez des modifications au code du côté du corps principal, mais changer le modificateur d'accès ne devrait normalement pas conduire à un bogue [^ 1].
[^ 1]: Bien sûr, ce serait un problème si le champ rendu public pour les tests (je veux le rendre privé) est accédé par le code de production, mais si vous utilisez @ VisibleForTesting
, vous pouvez distinguer s'il s'agit d'un champ auquel vous pouvez accéder. Tu devrais être capable de.
// Old
//Code du corps
class Spam {
// ...
private Foo field;
// ...
}
//Code de test
x = Deencapsulation.getField(obj, "field");
// New
// Old
//Code du corps
class Spam {
// ...
@VisibleForTesting
Foo field;
// ...
}
//Code de test
x = obj.field;
@ Tested
et @ Injectable
JMockit a un mécanisme DI pour les tests.
Si vous utilisez Deencapsulation.setField
pour simuler l'objet testé, il sera remplacé par DI.
4 Instantiation and injection of tested classes
.newMockInstance()
Assurez-vous d'obtenir l'instance fictive avec @Mocked. Une réécriture simple n'est pas possible.
Dans les anciennes versions, JMockit a été remplacé par n'importe quelle méthode,
Les méthodes natives telles que System.currentTimeMillis
ne sont pas remplacées dans la version actuelle.
Reconcevez votre test pour éliminer la nécessité du remplacement lui-même ou remplacez les méthodes non natives.
La partie du produit mis à niveau qui utilise «System.currentTimeMillis» a été modifiée pour remplacer «nouvelle date» à la place.
Bien que cela ne soit pas explicitement indiqué dans la documentation, il existe un modèle qui ** fonctionnait bien dans l'ancienne version et se compilait mais ne fonctionnait pas dans la nouvelle version **.
Si vous écrivez un autre appel de méthode comme argument de méthode ou valeur de retour dans «Attentes», il se peut qu'il ne soit pas bien remplacé. Les arguments et les valeurs de retour doivent être affectés à des variables temporaires en dehors des «attentes». Il vaut mieux les séparer du point de vue de la lisibilité.
// NG
new Expectations() {{
foo.method(getX(), getY()); result = getHogeList();
}};
// OK
X x = getX();
Y y = getY();
HogeList hogeList = getHogeList();
new Expectations() {{
foo.method(x, y); result = hogeList;
}};
Si vous souhaitez remplacer uniquement une méthode spécifique d'une classe ou uniquement une méthode d'une instance spécifique, spécifiez la classe ou l'instance dans «Attentes».
À ce stade, si vous mélangez des arguments ʻAttentes` avec des appels de méthode non liés, vous ne pourrez peut-être pas vous moquer correctement. Séparez les appels de méthode non pertinents dans un autre «Attentes». Il vaut mieux les séparer du point de vue de la lisibilité.
// NG
new Expectations(foo) {{
foo.method(x, y); result = v;
other.othersMethod(); result = value; //Cette méthode ne change pas
}};
// OK
new Expectations(foo) {{
foo.method(x, y); result = v;
}};
new Expectations() {{ //Diviser les attentes
other.othersMethod(); result = value;
}};
Recommended Posts