[JAVA] Notes pour ceux qui vivent avec JMockit

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.

Qu'est-ce que JMockit

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

Caractéristiques de la mise à niveau de la version JMockit

Une version mineure sera publiée tous les quelques mois. Vous pouvez consulter les versions précédentes de cette page:

JMockit - Development history

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.

Mockito est recommandé sur JMockit!

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.

Flux de travail de mise à niveau de version

Vous pouvez mettre à niveau par les opérations suivantes.

  1. Augmentez la version de JMockit
  2. Réécrire les fonctions qui ont été supprimées lors de la mise à niveau de la version
  3. Compilez
  4. Modifiez le code jusqu'à ce qu'il se compile
  5. Exécution des tests
  6. Modifiez le code jusqu'à ce que le test réussisse

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 tutoriel

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.

JMockit - Tutorial

Principales fonctions obsolètes et méthodes de réécriture

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.).

Comment exécuter le test

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) Annotation

Comme mentionné ci-dessus, il est obsolète, supprimez-le donc.

1 argument 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 avecminTimes = 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.

1. Changez le champ en 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;

2. Utilisez @ 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.

Des méthodes telles que "System" sont simulées

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.

Rédaction de styles à éviter dans la dernière version

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 **.

Ecrire un autre appel de méthode dans l'argument / la valeur de retour

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;
}};

Ecrire une méthode non pertinente dans «Attentes» avec des arguments

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

Notes pour ceux qui vivent avec JMockit
[Pour ceux qui créent des portefeuilles] Fonction de recherche créée avec ransack
[Pour ceux qui créent des portefeuilles] Comment utiliser binding.pry avec Docker
Consulter les notes de la classe java.util.Scanner
Résumé de la qualification Java Programmer Gold SE 8 (pour ceux qui connaissent Java)
Consultez les notes de la classe java.util.Optional
Une note de révision pour la classe java.util.Objects
Consulter les notes du package java.time.temporal
Accès avec Selenium comme contre-mesure pour navigator.webdriver
Une note pour quand quelqu'un qui était Java Java jusqu'à hier est venu toucher Scala
Créez un modèle pour le widget iOS14 avec la configuration d'intention.
[Bases de Java] Créons un triangle avec une instruction for
Remarque sur l'initialisation des champs dans le didacticiel Java
[Note] Créez un environnement Python3 avec Docker dans EC2
[Pour ceux qui créent des portfolios] Comment utiliser font-awesome-rails
[Note] Créez un environnement Java à partir de zéro avec docker
(Pour moi-même) Construisez un laboratoire git avec ubuntu 18.04 + docker pour la maison (Remarque)
Un jeu Janken à deux joueurs avec Java où les threads se jouent les uns contre les autres
Aux personnes qui ont supprimé un document dans Firestore, mais la sous-collection ne disparaît pas