78 votes

Mockito, JUnit et Spring

Je n'ai commencé à connaître Mockito qu'aujourd'hui. J'ai écrit quelques tests simples (avec JUnit, voir ci-dessous), mais je n'arrive pas à comprendre comment utiliser l'objet mock dans les managed beans de Spring. Qu'est-ce que meilleures pratiques pour travailler avec Spring. Comment injecter une dépendance fantaisie dans mon bean ?

Vous pouvez sauter cette étape jusqu'à retour à ma question .

Tout d'abord, ce que j'ai appris. C'est un très bon article Les mocks ne sont pas des stubs qui explique les bases (les vérifications de Mock vérification du comportement pas vérification des états ). Il y a ensuite un bon exemple ici Mockito et ici Moquerie plus facile avec mockito . Nous avons expliqué que les objets fantaisie de Mockito sont à la fois simuler y talon .

Ici Mockito et ici Matchers vous trouverez d'autres exemples.

Ce test

@Test
public void testReal(){
    List<String> mockedList = mock(List.class);
     //stubbing
     //when(mockedList.get(0)).thenReturn("first");

    mockedList.get(anyInt());
    OngoingStubbing<String> stub= when(null);
    stub.thenReturn("first");

    //String res = mockedList.get(0);
                //System.out.println(res);

     //you can also verify using argument matcher
     //verify(mockedList).get(anyInt());

    verify(mockedList);
    mockedList.get(anyInt());
}

fonctionne très bien.

Revenons à ma question. Ici Injection de mocks Mockito dans un bean Spring quelqu'un essaie d'utiliser Springs ReflectionTestUtils.setField() mais alors ici Tests d'intégration de Spring, création de Mock Objects nous avons une recommandation pour changement Le contexte du printemps.

Je n'ai pas vraiment compris les deux derniers liens... Quelqu'un peut-il m'expliquer quel est le problème de Spring avec Mockito ? Quel est le problème de cette solution ?

@InjectMocks
private MyTestObject testObject

@Mock
private MyDependentObject mockedObject

@Before
public void setup() {
        MockitoAnnotations.initMocks(this);
}

https://stackoverflow.com/a/8742745/1137529

EDIT : Je n'ai pas été très clair. Je vais fournir 3 exemples de code pour me clarifier : Supposons que nous ayons un haricot HelloWorld avec une méthode printHello() et le haricot HelloFacade avec la méthode sayHello qui transmet les appels à la méthode HelloWorld printHello() .

Le premier exemple utilise le contexte de Spring et sans programme d'exécution personnalisé, en utilisant ReflectionTestUtils pour l'injection de dépendances (DI) :

public class Hello1Test  {
private ApplicationContext ctx;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    this.ctx = new ClassPathXmlApplicationContext("META-INF/spring/ServicesImplContext.xml");
}

@Test
public void testHelloFacade() {
    HelloFacade obj = (HelloFacade) ctx.getBean(HelloFacadeImpl.class);
    HelloWorld mock = mock(HelloWorld.class);
    doNothing().when(mock).printHello();

    ReflectionTestUtils.setField(obj, "hello", mock);
    obj.sayHello();

    verify(mock, times(1)).printHello();
}

}

Comme @Noam l'a fait remarquer, il existe un moyen de l'exécuter sans faire appel explicitement à MockitoAnnotations.initMocks(this); . J'abandonnerai également l'utilisation du contexte de Spring sur cet exemple.

@RunWith(MockitoJUnitRunner.class)
public class Hello1aTest {

@InjectMocks
private HelloFacade obj =  new HelloFacadeImpl();

@Mock
private HelloWorld mock;

@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

Une autre façon de procéder

public class Hello1aTest {

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
}

@InjectMocks
private HelloFacadeImpl obj;

@Mock
private HelloWorld mock;

@Test
public void testHelloFacade() {
    doNothing().when(mock).printHello();
    obj.sayHello();
}

}

Rien, dans l'exemple précédent, nous devons instancier manuellement HelloFacadeImpl et l'assigner à HelloFacade, car HelloFacade est une interface. Dans le dernier exemple, nous pouvons simplement déclarer HelloFacadeImpl et Mokito l'instanciera pour nous. L'inconvénient de cette approche est que maintenant, l'unité sous test est impl-class et non interface.

1 votes

Est il y a un problème avec la solution ? L'article de blog dont vous faites le lien n'utilise pas @InjectMocks (relativement récent, bien qu'il soit antérieur à cet article de blog). Dans certaines circonstances, il peut être nécessaire de réorganiser les définitions des haricots. Je ne suis pas sûr de la question, en fin de compte.

0 votes

Spring n'a aucun problème avec Mockito. Ou vice versa.

0 votes

Je pense que dans la plupart des cas, vous devriez tester contre l'implémentation réelle plutôt que contre l'interface.

0voto

jhericks Points 2523

La différence entre le fait de devoir ou non instancier votre @InjectMocks se trouve dans la version de Mockito, et non dans le fait que vous utilisiez le MockitoJunitRunner ou le MockitoJunitRunner. MockitoAnnotations.initMocks . En 1.9, qui gérera également l'injection de certains constructeurs de votre @Mock il se chargera de l'instanciation pour vous. Dans les versions antérieures, vous devez l'instancier vous-même.

C'est ainsi que je fais les tests unitaires de mes beans Spring. Il n'y a pas de problème. Les gens rencontrent des difficultés lorsqu'ils veulent utiliser les fichiers de configuration de Spring pour injecter les objets fantaisie, ce qui va à l'encontre de l'objectif des tests unitaires et des tests d'intégration.

Et bien sûr l'unité testée est un Impl. Vous devez tester une chose concrète, non ? Même si vous la déclarez comme une interface, vous devez instancier la chose réelle pour la tester. Maintenant, vous pourriez vous lancer dans les spies, qui sont des enveloppes de stub/mock autour d'objets réels, mais cela devrait être réservé aux cas particuliers.

0 votes

Eh bien, je realmente Je veux tester mon "contrat" qui est défini dans l'interface. Si mon Impl a une méthode publique qui n'est pas exposée dans l'interface, pour moi c'est comme une méthode privée et j'ai tendance à l'ignorer.

0 votes

Mais qu'est-ce qui remplit le "contrat" ? Le diablotin, non ? C'est donc ce que vous testez. A quoi cela ressemblerait-il de tester juste l'interface ? Vous ne pouvez pas instancier une interface.

0 votes

J'ai ouvert une nouvelle question sur ce sujet stackoverflow.com/questions/10937763/

0voto

luboskrnac Points 5279

Si vous souhaitez migrer votre projet vers Spring Boot 1.4, vous pouvez utiliser la nouvelle annotation @MockBean pour avoir simulé MyDependentObject . Grâce à cette fonctionnalité, vous pouvez supprimer la fonction de Mockito @Mock y @InjectMocks annotations de votre test.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X