Le but de cet article est une note de mes recherches sur jmockit 1.48.
document http://jmockit.github.io/index.html
Code source https://github.com/jmockit/jmockit1
JavaDoc https://repo1.maven.org/maven2/org/jmockit/jmockit/1.48/jmockit-1.48-javadoc.jar
** Environnement de vérification ** java version "1.8.0_202" Eclipse IDE for Enterprise Java Developers. Version: 2019-03 (4.11.0) Build id: 20190314-1200 JUnit4
Lors de l'exécution de tests unitaires à l'aide de xUnit, les pièces dépendantes peuvent être un problème et difficiles à tester.
Par exemple, dans les cas suivants. -Lorsqu'il est difficile de renvoyer un contenu arbitraire à la cible de test en raison de pièces dépendantes
Dans ces cas, vous pouvez faciliter les tests unitaires en utilisant la méthode créée par jmockit au lieu des parties dépendantes.
En utilisant jmockit, il est possible de transmettre une valeur pratique pour le test à la cible de test au lieu de la partie dépendante, et d'enregistrer et de vérifier comment la partie dépendante a été appelée à partir de la cible de test. Je vais.
(1) Téléchargez Jar à partir de ce qui suit et faites-y référence à partir du projet. https://mvnrepository.com/artifact/org.jmockit/jmockit
Ou pour Maven, ajoutez ce qui suit à pom.xml
<!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.48</version>
<scope>test</scope>
</dependency>
(2) Ajouter un cas de test JUnit
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import mockit.Mock;
import mockit.MockUp;
public class SimpleTest {
@Test
public void test() {
new MockUp<java.lang.Math>() {
@Mock
public double random() {
//Toujours 2.Renvoie aléatoire 5()Méthode
return 2.5;
}
};
assertEquals(2.5, Math.random(), 0.1);
assertEquals(2.5, Math.random(), 0.1);
}
}
(3) Dans la configuration d'exécution au moment de l'exécution de junit, ajoutez "-javaagent: jmockit-1.48.jar" à l'argument VM et exécutez.
Voir ci-dessous pour plus de détails sur la façon de procéder: http://jmockit.github.io/tutorial/Introduction.html#runningTests
** Un événement **
** Trace d'erreur **
java.lang.Exception: Method testVerifications should have no parameters
at org.junit.runners.model.FrameworkMethod.validatePublicVoidNoArg(FrameworkMethod.java:76)
at org.junit.runners.ParentRunner.validatePublicVoidNoArgMethods(ParentRunner.java:155)
//Abréviation
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
** Cause ** "-Javaagent: jmockit-1.48.jar" n'est pas ajouté à l'argument VM.
Parfois, il y a une déclaration selon laquelle il est important de faire le chemin de classe de construction dans l'ordre jmockit → junit. https://stackoverflow.com/questions/32817982/jmockit-wasnt-properly-initialized?rq=1
Ce n'est probablement pas un problème dans la version actuelle, ou c'est parce que "-javaagent: jmockit-X.XX.jar" n'est pas ajouté à l'argument VM.
En outre, il semble y avoir un moyen d'utiliser "@ RunWith (JMockit.class)" comme une autre solution à l'ordre du chemin de la classe de construction, mais au moins à partir de 1.48, cet attribut n'existe pas.
https://github.com/jmockit/jmockit1/issues/554
Mocking La simulation fournit un mécanisme pour séparer la classe testée de (une partie de) ses dépendances. Utilisez les annotations @ Mocked / @ Injectable / @ Capturing pour créer une instance simulée. Les instances simulées peuvent être définies sur le comportement attendu dans Expectations (#expectations) et peuvent être vérifiées dans Verifications (#verifications) comment l'instance simulée a été exécutée.
Il est possible de se moquer en utilisant l'annotation @Mocked comme paramètre d'une méthode de cas de test ou un champ d'une classe de cas de test. Si vous utilisez l'annotation @Mocked, toutes les instances du même type seront simulées pendant la durée du test qui l'utilise.
Tout type peut être simulé à l'exception du type primitif et du type de tableau.
Voyons maintenant comment utiliser la classe suivante comme objet simulé.
** Cible de test **
package SampleProject;
public class Hoge001 {
public int hoge(int x, int y) {
return x + y;
}
public String hoge(String x) {
return "test" + x;
}
}
** Exemple d'utilisation de Mocked **
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge001;
import mockit.Expectations;
import mockit.Mocked;
public class Test001 {
//Lorsque vous n'utilisez pas de maquette...
@Test
public void test0() {
Hoge001 hoge = new Hoge001();
assertEquals(11, hoge.hoge(5,6));
assertEquals("testxxx", hoge.hoge("xxx"));
}
//Vous pouvez créer une instance simulée en la spécifiant comme paramètre de la méthode de test
@Test
public void test1(@Mocked Hoge001 mock) {
new Expectations() {{
//hoge est x=5, y =S'il est appelé par 6, renvoie 99 la première fois
mock.hoge(5,6);
result = 99;
}};
//Si vous spécifiez un résultat de méthode dans les attentes, cette valeur est récupérée
assertEquals(99, mock.hoge(5,6));
//Valeur par défaut si les attentes ne spécifient pas de résultat de méthode(null)Devient
assertEquals(null, mock.hoge("xxx"));
// @Si vous utilisez Mocked, toutes les instances applicables seront simulées pendant la durée du test.
Hoge001 hoge = new Hoge001();
assertEquals(99, hoge.hoge(5,6));
assertEquals(null, hoge.hoge("xxx"));
}
}
test0 () est un cas de test lorsqu'il n'est pas simulé, et test1 () est un cas de test utilisant @Mocked comme paramètre. Pendant le scénario de test de test1 (), toutes les instances Hoge001 sont des instances simulées.
Le test suivant confirme qu'il sera simulé même si vous n'avez pas créé l'instance directement dans le scénario de test.
** Cible de test ** La cible du test est Hoge002, qui est utilisée en créant une instance de Hoge001.
package SampleProject;
public class Hoge002 {
public int test(int x , int y) {
Hoge001 hoge1 = new Hoge001();
return hoge1.hoge(x*2, y*2);
}
}
** Code de test **
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge001;
import SampleProject.Hoge002;
import mockit.Expectations;
import mockit.Mocked;
public class Test001_3 {
//Vous pouvez créer une instance simulée en la spécifiant comme paramètre de la méthode de test
@Test
public void test1(@Mocked Hoge001 mock) {
new Expectations() {{
//hoge est x=10, y =S'il est appelé par 12, la première fois renvoie 99
mock.hoge(10,12);
result = 99;
}};
Hoge002 hoge2 = new Hoge002();
assertEquals(99, hoge2.test(5,6));
}
}
Si vous exécutez ce cas de test, vous pouvez voir que le Hoge001 créé par Hoge002 est une version simulée.
Si vous utilisez @Mocked pour un champ de classe, tous les tests de la classe se moqueront de la classe cible.
** Code de test **
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge001;
import SampleProject.Hoge002;
import mockit.Expectations;
import mockit.Mocked;
public class Test001_2 {
//Les instances simulées peuvent être utilisées dans chaque cas de test en les spécifiant comme champs de classe de test.
@Mocked
private Hoge001 fieldMocked;
@Test
public void test1() {
new Expectations() {{
fieldMocked.hoge(anyInt, anyInt);
result = 100;
}};
assertEquals(100, fieldMocked.hoge(1,2));
}
@Test
public void test2() {
new Expectations() {{
//hoge est x=10, y =S'il est appelé par 12, la première fois renvoie 99
fieldMocked.hoge(10,12);
result = 99;
}};
Hoge002 hoge2 = new Hoge002();
assertEquals(99, hoge2.test(5,6));
}
}
Supposons que vous ayez des fonctionnalités fournies à l'aide de nombreux objets différents. Il n'est pas rare d'avoir des appels tels que "obj1.getObj2 (...). GetYetAnotherObj (). DoSomething (...)". Regardons un exemple de simulation dans ce cas.
Dans l'exemple ci-dessous, le code consiste à vérifier si la moquerie est effectuée dans une méthode qui renvoie un objet tel que mock.getDepend1 (). Output ().
** Classe sous test **
package SampleProject;
public class Depend001 {
private String prefix;
public Depend001(String p) {
this.prefix = p;
}
public String output(String msg) {
return this.prefix + msg;
}
}
package SampleProject;
public class Hoge003 {
private Depend001 d1;
public Depend001 d2;
public Hoge003() {
}
public Hoge003(Depend001 depend1, Depend001 depend2) {
this.d1 = depend1;
this.d2 = depend2;
}
public String output() {
String ret = "";
ret = ret + this.d1.output("test1") + "\n";
ret = ret + this.d2.output("test2") + "\n";
return ret;
}
public Depend001 getDepend1() {
return this.d1;
}
}
** Code de test **
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Mocked;
public class Test002 {
@Test
public void test1(@Mocked Hoge003 mock) {
new Expectations() {{
mock.getDepend1().output(anyString);
result = "abcde";
}};
assertEquals("abcde", mock.getDepend1().output("abc"));
}
}
Comme dans l'exemple ci-dessus, il a été confirmé que se moquer de la classe Hoge003 d'origine modifie le comportement attendu de la méthode cible sans se moquer explicitement de la branche et de la feuille Depend001.
Comme l'annotation @Mocked, il s'agit d'une annotation pour se moquer, mais la différence avec l'annotation @Mocked est que la maquette est limitée à une seule instance. Il peut également être utilisé pour l'injection automatique dans l'objet sous test en combinaison avec l'annotation @Tested.
Pour voir la différence entre les annotations @Mocked et @Injectable, remplacez le code de test utilisé dans l'annotation @Mocked par @Injectable et vérifiez.
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge001;
import mockit.Expectations;
import mockit.Injectable;
public class Test004 {
//Vous pouvez créer une instance simulée en la spécifiant comme paramètre de la méthode de test
@Test
public void test1(@Injectable Hoge001 mock) {
new Expectations() {{
//hoge est x=5, y =S'il est appelé par 6, renvoie 99 la première fois
mock.hoge(5,6);
result = 99;
}};
//Si vous spécifiez un résultat de méthode dans les attentes, cette valeur est récupérée
assertEquals(99, mock.hoge(5,6));
//Valeur par défaut si les attentes ne spécifient pas de résultat de méthode(null)Devient
assertEquals(null, mock.hoge("xxx"));
// @Toutes les instances applicables ne sont pas simulées, comme c'est le cas avec Mocked.
Hoge001 hoge = new Hoge001();
assertEquals(11, hoge.hoge(5,6));
assertEquals("testxxx", hoge.hoge("xxx"));
}
}
Lors de l'utilisation de l'annotation @Mocked, elle était simulée à chaque fois qu'une instance de la classe cible était créée pendant la période de test, mais en utilisant @Injectable, le nombre d'instances simulées est limité à une. Vous pouvez voir que vous le faites.
Vérifions l'exemple qui injecte une maquette dans l'objet à tester spécifié par l'annotation @Tested. Il existe deux méthodes, l'une consiste à injecter dans l'argument du constructeur de l'objet à tester spécifié par @Tested, et l'autre à injecter dans le champ à tester.
** Comment injecter dans les arguments du constructeur ** Voici un exemple de spécification de depend1 et depend2, qui sont les arguments du constructeur de Hoge003 (Depend001 depend1, Depend001 depend2).
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Depend001;
import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Tested;
public class Test003 {
@Tested
Hoge003 target;
@Injectable
Depend001 depend1;
@Injectable
Depend001 depend2;
@Test
public void test1() {
new Expectations() {{
depend1.output(anyString);
result = "abcde";
depend2.output(anyString);
result = "xxxxx";
}};
assertEquals("abcde\nxxxxx\n", target.output());
}
}
** Comment injecter sur le terrain ** Voici un exemple à injecter dans les champs d1 et d2 de l'objet Hoge003.
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Depend001;
import SampleProject.Hoge003;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Tested;
public class Test003B {
@Tested
Hoge003 target;
@Injectable
Depend001 d1;
@Injectable
Depend001 d2;
@Test
public void test1() {
new Expectations() {{
d1.output(anyString);
result = "abcde";
d2.output(anyString);
result = "xxxxx";
}};
assertEquals("abcde\nxxxxx\n", target.output());
}
}
En utilisant l'élément value de l'annotation @Injectable, il est possible d'injecter dans le champ ou la construction du type primitif à tester spécifié par l'annotation @Tested.
package jmockittest;
import org.junit.Test;
import SampleProject.Depend001;
import mockit.Injectable;
import mockit.Tested;
public class Test005 {
@Tested
Depend001 tested;
@Test
public void test1(@Injectable("abc") String p) {
//Sortie de ce qui suit
// abcaaa
System.out.println(tested.output("aaa"));
}
@Test
public void test2(@Injectable("abc") String prefix) {
//Sortie de ce qui suit
// abcbbb
System.out.println(tested.output("bbb"));
}
}
test1 injecte en spécifiant l'argument constructeur p de l'objet à tester, et test2 injecte en spécifiant le préfixe de champ de l'objet à tester.
type | Nom | Éléments optionnels et description | Valeur spécifiée |
---|---|---|---|
boolean | availableDuringSetup | La classe testée est la méthode de configuration du test (c'est-à-dire@Avant ou@Indique s'il est instancié et initialisé avant (une méthode annotée comme BeforeMethod) est exécuté, ou après eux. | false |
boolean | fullyInitialized | Indique qu'une valeur doit être attribuée à chaque champ non final d'un objet testé éligible pour l'injection. Si vous utilisez Spring, l'utilisation est décrite à la page suivante. https://stackoverflow.com/questions/25856210/injecting-only-some-properties-mocking-others | false |
boolean | global | Crée une seule instance nommée de la classe testée pour indiquer si elle sera utilisée tout au long du test. | false |
String | value | Champ à tester/Si le type de paramètre est une chaîne, un type primitif ou wrapper, un type numérique ou un type d'énumération, spécifiez une valeur littérale. | "" |
** Code de test pour vérifier la disponibilité pendant la configuration et global **
package jmockittest;
import org.junit.Before;
import org.junit.Test;
import SampleProject.Hoge001;
import mockit.Tested;
public class Test007 {
@Tested(availableDuringSetup=true, global=true)
Hoge001 tested;
@Before
public void before()
{
//Autre que null: si availableDuringSetup est false, il sera nul.
System.out.println("before:" + tested);
}
@Test
public void test1() {
//Non nul
System.out.println("test1:" + tested);
}
@Test
public void test2() {
//Vous pouvez voir que le même objet que test1 est utilisé sauf pour null
System.out.println("test2:" + tested);
}
}
Vous pouvez vous moquer de la classe ou de l'interface par défaut à l'aide de l'annotation @Capturing. L'exemple ci-dessous est un exemple qui crée une méthode simulée pour une interface plutôt qu'une classe d'implémentation individuelle.
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import mockit.Capturing;
import mockit.Expectations;
public class Test006 {
public interface Service { int doSomething(); }
final class ServiceImpl implements Service { public int doSomething() { return 1; } }
public final class TestedUnit {
private final Service service1 = new ServiceImpl();
private final Service service2 = new Service() { public int doSomething() { return 2; } };
public int businessOperation() {
return service1.doSomething() + service2.doSomething();
}
}
//Créer une maquette pour une interface ou une classe par défaut
@Test
public void test1(@Capturing Service anyService) {
new Expectations() {{ anyService.doSomething(); returns(3, 4); }};
int result = new TestedUnit().businessOperation();
assertEquals(7, result);
}
}
Expectations Les attentes définissent le comportement attendu des objets fictifs associés à un test particulier.
Pendant les attentes, vous pouvez spécifier les paramètres à spécifier pour la méthode d'objet fictif et la valeur à renvoyer. Dans l'exemple ci-dessous, il s'agit d'un exemple de définition de la valeur renvoyée lorsque les méthodes "String hoge (String)" et "int hoge (int, int)" sont exécutées.
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import SampleProject.Hoge001;
import mockit.Delegate;
import mockit.Expectations;
import mockit.Mocked;
public class Test008 {
//Si vous spécifiez le résultat de la méthode dans les attentes, assurez-vous que la valeur est obtenue.
@Test
public void test1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge("test");
result = "abcde";
mock.hoge(5,6);
result = 99;
result = 100;
result = 101;
}};
// mock.hoge("test")Obtenez la valeur attendue lors de l'exécution
assertEquals("abcde", mock.hoge("test"));
// mock.hoge(5,6)Obtenez la valeur attendue lors de l'exécution
//Obtenez la première valeur définie dans les attentes
assertEquals(99, mock.hoge(5,6));
//Obtenez la deuxième valeur définie dans les attentes
assertEquals(100, mock.hoge(5,6));
//Obtenez la troisième valeur définie dans les attentes
assertEquals(101, mock.hoge(5,6));
//Obtenez la dernière valeur définie dans les attentes
assertEquals(101, mock.hoge(5,6));
//Obtenez la dernière valeur définie dans les attentes
assertEquals(101, mock.hoge(5,6));
//Si les arguments sont différents, ce sera la valeur initiale
assertEquals(0, mock.hoge(7,6));
}
}
Plusieurs résultats peuvent être répertoriés ensemble dans les retours, comme indiqué ci-dessous.
new Expectations() {{
mock.hoge("test");
result = "abcde";
mock.hoge(5,6);
returns(99, 100, 101);
}};
Dans l'exemple précédent, la valeur de retour n'était renvoyée que lorsque la valeur d'un argument spécifique était acceptée, mais la valeur de l'argument peut être définie de manière flexible en spécifiant n'importe quel ~ ou avec ~ comme argument.
Les attentes ont plusieurs champs qui représentent n'importe quel but.
type | name |
---|---|
Object | any |
Boolean | anyBoolean |
Byte | anyByte |
Character | anyChar |
Double | anyDouble |
Float | anyFloat |
Integer | anyInt |
Long | anyLong |
Short | anyShort |
String | anyString |
Un exemple utilisant le champ any est le suivant.
@Test
public void test1_1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyString);
result = "abcde";
mock.hoge(anyInt, anyInt);
result = 99;
}};
// mock.hoge("test")Obtenez la valeur attendue lors de l'exécution
assertEquals("abcde", mock.hoge("test"));
assertEquals("abcde", mock.hoge("hogehoget"));
// mock.hoge(5,6)Obtenez la valeur attendue lors de l'exécution
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
}
Vous pouvez également combiner des valeurs d'argument fixes avec des valeurs d'argument arbitraires. Dans l'exemple ci-dessous, créez une méthode fictive qui renvoie 10 pour hoge (5,6) et 99 sinon.
@Test
public void test1_2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(5,6);
result = 10;
mock.hoge(anyInt, anyInt);
result = 99;
}};
// mock.hoge(5,6)Obtenez la valeur attendue lors de l'exécution
assertEquals(10, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
}
Lors de la combinaison avec la valeur d'un argument, entrez d'abord la valeur fixe.
En utilisant la méthode with ~, il est possible de juger de manière flexible si elle correspond ou non à la méthode fictive spécifiée dans les attentes.
Méthode | La description |
---|---|
with(Delegate<? super T> objectWithDelegateMethod) | Utilisez la méthode déléguée pour déterminer si les arguments correspondent. Si la valeur de retour de la méthode déléguée est true, cela signifie une correspondance. |
withEqual(T arg) | Vérifie si la valeur spécifiée correspond à l'argument lors de l'exécution du simulacre. En général, n'utilisez pas cette méthode, mais utilisez la méthode de transmission de la valeur de l'argument souhaité. |
withEqual(double value, double delta) | On suppose que la correspondance est proche de la valeur spécifiée par delta. |
withEqual(float value, double delta) | On suppose que la correspondance est proche de la valeur spécifiée par delta. |
withAny(T arg) | Pensez à utiliser anyBoolean, anyByte, anyChar, anyDouble, anyFloat, anyInt, anyLong, anyShort, anyString, any. |
withNotEqual(T arg) | Si la valeur est différente de la valeur spécifiée, elle est considérée comme concordante. |
withNotNull() | Si la valeur spécifiée est non NULL, elle est considérée comme correspondant. |
withNull() | Si la valeur spécifiée est NULL, elle est supposée correspondre |
withInstanceOf(Class |
Assurez-vous qu'il s'agit d'une instance de la classe spécifiée. |
withInstanceLike(T object) | Assurez-vous qu'il s'agit d'une instance de la même classe que l'objet spécifié. withInstanceOf(object.getClass())Sera équivalent à |
withSameInstance(T object) | Assurez-vous qu'il s'agit exactement de la même instance |
withPrefix(T text) | Si un caractère particulier est inclus, il est considéré comme une correspondance |
withSubstring(T text) | Si le début correspond au caractère spécifié, il est considéré comme correspondant. |
withSuffix(T text) | Si la fin correspond au caractère spécifié, il est considéré comme correspondant. |
withMatch(T regex) | Vous pouvez spécifier s'il faut faire correspondre avec une expression régulière |
En utilisant la méthode déléguée avec with, il est possible de juger si les arguments fictifs correspondent dans la méthode.
@Test
public void test1_4(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(with(new Delegate<Integer>() {
@Mock boolean validate(int value) {
return value >= 0;
}
}),anyInt);
result = 99;
}};
//Puisque x est positif, il correspond à la valeur définie dans le simulacre
assertEquals(99, mock.hoge(1,2));
//Puisque x est négatif, il ne correspond pas à la valeur définie dans le simulacre
assertEquals(0, mock.hoge(-1,2));
}
Fondamentalement, il est préférable d'utiliser le littéral tel quel que d'utiliser withEqual. Cependant, si vous souhaitez utiliser une virgule flottante, vous devez utiliser withEqual.
class testWithEqual {
int test1(double v) {
return 1000;
}
int test2(int v) {
return 2000;
}
}
@Test
public void test_withEqual1(@Mocked testWithEqual mock) {
new Expectations() {{
mock.test2(withEqual(100));
result = 99;
}};
//Maquette assortie.test2(100)Pareil que
assertEquals(99, mock.test2(100));
//Ça ne correspond pas
assertEquals(0, mock.test2(101));
}
@Test
public void test_withEqual2(@Mocked testWithEqual mock) {
new Expectations() {{
mock.test1(withEqual(100, 1));
result = 99;
}};
//Rencontre
assertEquals(99, mock.test1(100.0));
assertEquals(99, mock.test1(101.0));
assertEquals(99, mock.test1(99.0));
//Ça ne correspond pas
assertEquals(0, mock.test1(101.1));
assertEquals(0, mock.test1(98.99));
}
Vous pouvez utiliser withInstanceOf, withInstanceOf, withSameInstance pour voir si cela correspond à une instance particulière.
class classA {
}
class classB {
}
class classX {
public int method1(Object obj) {
return 999;
}
}
@Test
public void test_withInst1(@Mocked classX mock) {
new Expectations() {{
mock.method1(withInstanceOf(classA.class));
result = 99;
}};
//Rencontre
{
classA obj = new classA();
assertEquals(99, mock.method1(obj));
}
//Ça ne correspond pas
{
classB obj = new classB();
assertEquals(0, mock.method1(obj));
}
}
@Test
public void test_withInst2(@Mocked classX mock) {
new Expectations() {{
classA objA = new classA();
mock.method1(withInstanceLike(objA));
result = 99;
}};
//Rencontre
{
classA obj = new classA();
assertEquals(99, mock.method1(obj));
}
//Ça ne correspond pas
{
classB obj = new classB();
assertEquals(0, mock.method1(obj));
}
}
@Test
public void test_withInst3(@Mocked classX mock) {
classA obj1 = new classA();
new Expectations() {{
mock.method1(withSameInstance(obj1));
result = 99;
}};
//Rencontre
{
assertEquals(99, mock.method1(obj1));
}
//Ça ne correspond pas
{
classA obj2 = new classA();
assertEquals(0, mock.method1(obj2));
}
}
En utilisant withPrefix, withSubstring, withSuffix, withMatch, il est possible de vérifier si une partie de la chaîne de caractères correspond.
@Test
public void test_withString1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(withPrefix("abc"));
result = "test";
}};
//Le match suivant
assertEquals("test", mock.hoge("abc"));
assertEquals("test", mock.hoge("abcAA"));
//Les éléments suivants ne correspondent pas
assertEquals(null, mock.hoge("AAabc"));
assertEquals(null, mock.hoge("AabcA"));
assertEquals(null, mock.hoge("xx"));
}
@Test
public void test_withString2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(withSuffix("abc"));
result = "test";
}};
//Le match suivant
assertEquals("test", mock.hoge("abc"));
assertEquals("test", mock.hoge("AAabc"));
//Les éléments suivants ne correspondent pas
assertEquals(null, mock.hoge("abcAA"));
assertEquals(null, mock.hoge("AabcA"));
assertEquals(null, mock.hoge("xx"));
}
@Test
public void test_withString3(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(withSubstring("abc"));
result = "test";
}};
//Le match suivant
assertEquals("test", mock.hoge("abc"));
assertEquals("test", mock.hoge("abcAA"));
assertEquals("test", mock.hoge("AAabc"));
assertEquals("test", mock.hoge("AabcA"));
//Les éléments suivants ne correspondent pas
assertEquals(null, mock.hoge("xx"));
}
@Test
public void test_withString4(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(withMatch("[0-9]+"));
result = "test";
}};
//Le match suivant
assertEquals("test", mock.hoge("1234"));
//Les éléments suivants ne correspondent pas
assertEquals(null, mock.hoge("xxx"));
}
Il est possible de diviser la méthode fictive en fonction de la façon dont l'instance est créée dans les attentes. L'exemple suivant montre un exemple qui applique la méthode fictive uniquement à l'instance créée en exécutant "new TestA (10)".
class TestA {
public TestA(int x) {
}
public int hoge() {
return 99999;
}
}
@Test
public void test8(@Mocked TestA mock) {
new Expectations() {{
TestA t1 = new TestA(10);
t1.hoge();
result = 10;
}};
{
TestA obj = new TestA(10);
assertEquals(10, obj.hoge());
}
{
TestA obj = new TestA(99);
assertEquals(0, obj.hoge());
}
}
Vous pouvez lever une exception lors du traitement d'une méthode fictive. L'exemple suivant déclenche une exception IllegalArgumentException lors de l'exécution de la méthode hoge ().
//Un exemple de renvoi d'une exception de méthode dans les attentes
@Test
public void test2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(5,6);
result = 99;
result = new IllegalArgumentException("test");
}};
//Obtenez la première valeur définie dans les attentes
assertEquals(99, mock.hoge(5,6));
try {
//Obtenez la deuxième valeur définie dans les attentes
mock.hoge(5,6);
fail();
} catch (IllegalArgumentException ex) {
assertEquals("test", ex.getMessage());
}
}
Il est possible de spécifier le nombre d'exécutions d'une méthode en spécifiant times, maxTImes et minTimes dans les attentes.
Field | Description |
---|---|
tiems | Spécifie combien de fois la méthode est appelée pendant l'exécution. S'il est appelé un nombre de fois différent, une erreur se produira. |
maxTimes | Spécifie le nombre maximal de méthodes appelées. S'il est appelé plus de fois que cela, une erreur se produira. |
minTimes | Spécifie le nombre minimum de méthodes à appeler. Une erreur se produira si elle est appelée moins de ce nombre de fois. |
@Test
public void test4_1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
times = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
}
//Ce test échoue avec 2 invocations manquantes
@Test
public void test4_2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
times = 3;
}};
assertEquals(99, mock.hoge(3,6));
}
//Ce test entraîne un appel inattendu et une erreur
@Test
public void test4_3(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
times = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
assertEquals(99, mock.hoge(3,6));
}
@Test
public void test5_1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
minTimes = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
}
//Ce test échoue avec 2 invocations manquantes
@Test
public void test5_2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
minTimes = 3;
}};
assertEquals(99, mock.hoge(3,6));
}
@Test
public void test5_3(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
minTimes = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
assertEquals(99, mock.hoge(3,6));
}
@Test
public void test6_1(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
maxTimes = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
}
@Test
public void test6_2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
maxTimes = 3;
}};
assertEquals(99, mock.hoge(3,6));
}
//Ce test entraîne un appel inattendu et une erreur
@Test
public void test6_3(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt, anyInt);
result = 99;
maxTimes = 3;
}};
assertEquals(99, mock.hoge(5,6));
assertEquals(99, mock.hoge(99,1234));
assertEquals(99, mock.hoge(3,6));
assertEquals(99, mock.hoge(3,6));
}
Utilisez Deglegate si vous souhaitez modifier le résultat renvoyé par le simulacre en fonction des arguments lors de l'exécution de la méthode simulée. Dans l'exemple ci-dessous, nous créons une méthode fictive qui renvoie une valeur qui est le double de l'argument d'entrée.
@Test
public void test7(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt,anyInt);
result= new Delegate<Integer>() {
@SuppressWarnings("unused")
int aDelegateMethod(int x, int y) {
return x * 2 + y * 2;
}
};
}};
//Obtenez la première valeur définie dans les attentes
assertEquals(22, mock.hoge(5,6));
}
Il est possible d'utiliser Invocation comme premier paramètre de la méthode Delegate. Invocation propose les getters suivants:
Méthode | La description |
---|---|
getInvocationCount() | Nombre d'appels |
getInvocationIndex() | Obtenir l'index des appels en cours |
getInvokedArguments() | Récupère l'argument utilisé pour l'appel |
getInvokedInstance() | Instance de l'appel en cours. Null pour les méthodes statiques |
getInvokedMember() | Méthode d'appel/Obtenir le constructeur |
proceed(Object... replacementArguments) | Méthode réelle/Exécutez le constructeur |
@Test
public void testDelegate2(@Mocked Hoge001 mock) {
new Expectations() {{
mock.hoge(anyInt,anyInt);
result= new Delegate<Integer>() {
@SuppressWarnings("unused")
int aDelegateMethod(Invocation inv ,int x, int y) {
System.out.println("--------------------------------");
//Nombre d'appels
System.out.format("Invocation getInvocationCount %d \n", inv.getInvocationCount());
//Index de l'appel en cours
System.out.format("Invocation getInvocationIndex() %d \n", inv.getInvocationIndex());
//Obtenir l'argument
System.out.println("getInvokedArguments");
for(Object obj : inv.getInvokedArguments()) {
System.out.println(obj);
}
//Obtenez une instance
System.out.format("Invocation getInvokedInstance() %s \n", inv.getInvokedInstance().toString());
//Obtenez la méthode actuelle
System.out.format("Invocation getInvokedMember() %s \n", inv.getInvokedMember().toString());
//La méthode réelle peut être exécutée.
System.out.format("Invocation proceed %s \n", inv.proceed().toString());
//La méthode réelle peut être exécutée en altérant l'argument
System.out.format("Invocation proceed %s \n", inv.proceed(5,6).toString());
return 0;
}
};
}};
//Obtenez la première valeur définie dans les attentes
Hoge001 a = new Hoge001();
Hoge001 b = new Hoge001();
a.hoge(5,6);
a.hoge(45,63);
b.hoge(99,100);
}
Le journal de la console qui a exécuté ce qui précède est le suivant.
--------------------------------
Invocation getInvocationCount 1
Invocation getInvocationIndex() 0
getInvokedArguments
5
6
Invocation getInvokedInstance() SampleProject.Hoge001@2a2d45ba
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int)
Invocation proceed 11
Invocation proceed 11
--------------------------------
Invocation getInvocationCount 2
Invocation getInvocationIndex() 1
getInvokedArguments
45
63
Invocation getInvokedInstance() SampleProject.Hoge001@2a2d45ba
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int)
Invocation proceed 108
Invocation proceed 11
--------------------------------
Invocation getInvocationCount 3
Invocation getInvocationIndex() 2
getInvokedArguments
99
100
Invocation getInvokedInstance() SampleProject.Hoge001@675d3402
Invocation getInvokedMember() public int SampleProject.Hoge001.hoge(int,int)
Invocation proceed 199
Invocation proceed 11
Pour simuler uniquement certaines des méthodes au lieu de toutes, passez l'objet à Expectations comme suit:
@Test
public void test10() {
Hoge001 hoge = new Hoge001();
new Expectations(hoge) {{
hoge.hoge(5,6);
result = 99;
}};
//Renvoie le résultat du simulacre
assertEquals(99, hoge.hoge(5,6));
//Exécuter la méthode réelle
assertEquals(3, hoge.hoge(1,2));
assertEquals("testabc", hoge.hoge("abc"));
}
Verifications Vous pouvez utiliser Verifications, VerificationsInOrder et FullVerifications pour vérifier explicitement comment un objet fictif a été appelé.
@Test
public void test_v1(@Mocked Hoge001 mock) {
mock.hoge(1,2);
mock.hoge(2,3);
mock.hoge(4,5);
//
new Verifications() {{
mock.hoge(anyInt,anyInt);
times = 3;
mock.hoge(anyString);
times = 0;
}};
//Les vérifications considèrent les appels hors service ou les appels supplémentaires à passer
new Verifications() {{
mock.hoge(4,5);
mock.hoge(1,2);
}};
}
@Test
public void test_v2(@Mocked Hoge001 mock) {
mock.hoge(1,2);
mock.hoge(2,3);
mock.hoge(4,5);
//VerificationsInOrder entraînera une erreur si la commande est différente
/*
new VerificationsInOrder() {{
mock.hoge(4,5);
mock.hoge(1,2);
}};
*/
new VerificationsInOrder() {{
mock.hoge(1,2);
mock.hoge(4,5);
}};
}
@Test
public void test_v3(@Mocked Hoge001 mock) {
mock.hoge(1,2);
mock.hoge(2,3);
mock.hoge(4,5);
//Les vérifications complètes entraîneront une erreur si des appels supplémentaires sont effectués
/*
new FullVerifications() {{
mock.hoge(1,2);
mock.hoge(4,5);
}};
*/
new FullVerifications() {{
mock.hoge(1,2);
mock.hoge(2,3);
mock.hoge(4,5);
}};
//Cela passera même si l'ordre est différent
new FullVerifications() {{
mock.hoge(4,5);
mock.hoge(2,3);
mock.hoge(1,2);
}};
}
AvecCapture, vous pouvez obtenir une instance avec n'importe quel paramètre de List.
//Exemple de vérification des paramètres avec withCapture
@Test
public void test_v4(@Mocked Hoge001 mock) {
mock.hoge(1,2);
mock.hoge(2,3);
mock.hoge(4,5);
//
new Verifications() {{
List<Integer> argXList = new ArrayList<Integer>();
List<Integer> argYList = new ArrayList<Integer>();
mock.hoge(withCapture(argXList),withCapture(argYList));
assertEquals(3, argXList.size());
assertEquals(3, argYList.size());
assertEquals(1, (int)argXList.get(0));
assertEquals(2, (int)argXList.get(1));
assertEquals(4, (int)argXList.get(2));
assertEquals(2, (int)argYList.get(0));
assertEquals(3, (int)argYList.get(1));
assertEquals(5, (int)argYList.get(2));
}};
}
//Exemple de confirmation de la création d'instance avec withCapture
class Person {
public Person(String name , int age) {
}
}
@Test
public void test_v5(@Mocked Person mockPerson) {
new Person("Joe", 10);
new Person("Sara", 15);
new Person("Jack", 99);
//
new Verifications() {{
List<Person> created = withCapture(new Person(anyString, anyInt));
assertEquals(3, created.size());
}};
}
Faking API L'API Faking fournit un support pour la création de fausses implémentations. Fake cible généralement certaines méthodes et constructeurs de la classe Fake, et la plupart des autres méthodes et constructeurs restent inchangés.
Dans l'exemple suivant, seul Proc1 de la classe dans laquelle Proc1 et Proc2 existent est Fake.
package jmockittest;
import static org.junit.Assert.*;
import org.junit.Test;
import mockit.Mock;
import mockit.MockUp;
public class FakeTest {
class ClassA {
protected String Proc1() {
return "...Proc1";
}
public String Proc2() {
return "Proc2:" + this.Proc1();
}
}
@Test
public void test1() {
new MockUp<ClassA>() {
@Mock
String Proc1() {
System.out.print("Proc1");
return "xxx";
}
};
ClassA obj = new ClassA();
assertEquals("Proc2:xxx", obj.Proc2());
}
}
Pas possible avec 1.48. J'obtiens l'erreur suivante:
java.lang.IllegalArgumentException: Unsupported fake for private method ClassA#Proc1()Ljava/lang/String; found
at jmockittest.FakeTest$1.<init>(FakeTest.java:22)
at jmockittest.FakeTest.test1(FakeTest.java:22)
Probablement, il semble que cela a été fait avant et ne peut pas être fait. https://github.com/jmockit/jmockit1/issues/605
La falsification de la méthode statique est possible. L'exemple ci-dessous est un exemple de java.lang.Math.random qui renvoie toujours une valeur fixe.
@Test
public void test() {
new MockUp<java.lang.Math>() {
@Mock
public double random() {
//Toujours 2.Renvoie aléatoire 5()Méthode
return 2.5;
}
};
assertEquals(2.5, Math.random(), 0.1);
assertEquals(2.5, Math.random(), 0.1);
}
Il était possible de créer.
class ClassB {
final protected String Proc1() {
return "...Proc1";
}
public String Proc2() {
return "Proc2:" + this.Proc1();
}
}
@Test
public void test3() {
new MockUp<ClassB>() {
@Mock
String Proc1() {
System.out.print("Proc1");
return "xxx";
}
};
ClassB obj = new ClassB();
assertEquals("Proc2:xxx", obj.Proc2());
}
Il y a \ $ init, \ $ clinic, \ $ conseils comme méthodes spéciales dans la classe Fake. \ $ init cible le constructeur. \ $ clinic est destiné aux initialiseurs statiques. \ $ Advice représente toutes les méthodes de la classe cible.
** Cible de test **
ClassC.java
package SampleProject;
public class ClassC {
public static int sx;
private int x;
static {
sx = 999;
}
public ClassC(int x) {
this.x = x;
}
public String Proc1() {
System.out.format("ClassC Proc1 %d %d\n", sx, this.x);
return "...Proc1";
}
}
** Code de test **
@Test
public void test4() {
new MockUp<ClassC>() {
@Mock
void $clinit() {
//Confirmez que l'initialisation statique de ClassiC ne fonctionne pas
assertEquals(0, ClassC.sx);
}
@Mock
void $init(int x) {
assertEquals(100, x);
}
@Mock
Object $advice(Invocation inv) {
return "test";
}
};
ClassC obj = new ClassC(100);
assertEquals("test", obj.Proc1());
}
Il est possible d'utiliser [Invocation](en utilisant #invocation) comme premier paramètre de la méthode Fake. Vous trouverez ci-dessous un exemple qui l'utilise pour renvoyer une valeur fixe pour l'heure actuelle.
@Test
public void testTime() {
Calendar nowCalendar = Calendar.getInstance();
System.out.println("Date et heure actuelles: " + nowCalendar.getTime());
new MockUp<Calendar>() {
@Mock
Calendar getInstance(Invocation inv) {
Calendar cal = inv.proceed();
cal.set(Calendar.YEAR, 2018);
cal.set(Calendar.MONTH, 0);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR, 22);
cal.set(Calendar.MINUTE, 32);
cal.set(Calendar.SECOND, 12);
cal.set(Calendar.MILLISECOND, 512);
return cal;
}
@Mock
Calendar getInstance(Invocation inv, TimeZone zone, Locale aLocale) {
Calendar cal = inv.proceed();
cal.set(Calendar.YEAR, 2018);
cal.set(Calendar.MONTH, 0);
cal.set(Calendar.DAY_OF_MONTH, 1);
cal.set(Calendar.HOUR, 22);
cal.set(Calendar.MINUTE, 32);
cal.set(Calendar.SECOND, 12);
cal.set(Calendar.MILLISECOND, 512);
return cal;
}
};
final Calendar c = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
assertEquals("20180102103212512", sdf.format(c.getTime()));
}
Les résultats de la mesure de la couverture peuvent être générés en donnant des arguments VM dans la configuration d'exécution.
-Dcoverage-output=html -Dcoverage-srcDirs=..\SampleProject\src
Voir ci-dessous pour d'autres arguments. http://jmockit.github.io/tutorial/CodeCoverage.html
En tant que comportement non documenté, "-Dcoverage-output = xml" semble produire du XML.
J'ai fait des recherches jusqu'à présent, mais en regardant la discussion autour de la méthode privée sur GitHub et l'historique de l'abolition de l'historique des mises à jour, je pense que c'est un peu risqué à utiliser si vous n'êtes pas dans un environnement de test idéal complet et parfait. ..
De plus, j'ai également vérifié powermock + mockito ci-dessous.
** Essayez d'utiliser powermock-mockito2-2.0.2 ** https://qiita.com/mima_ita/items/3574a03b3379fb5f3c3c
Recommended Posts