Mockito @InjectMocks ne fonctionne pas pour les champs avec le même type

j'ai été très surpris de découvrir que suivre un exemple de code simple ne fonctionne pas pour toutes les versions de Mockito > 1.8.5

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    @Mock(name = "b2")
    private B b2;

    @InjectMocks
    private A a;

    @Test
    public void testInjection() throws Exception {
        assertNotNull(a.b2); //fails
        assertNull(a.b1); //also fails, because unexpectedly b2 mock gets injected here
    }

    static class A{
        private B b1;
        private B b2;
    }

    interface B{}
}

Dans la documentation javadoc (http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html) il y a une citation:

Note 1: Si vous avez des champs avec le même type (ou la même effacement), c'est il est préférable de nommer tous les champs @ annotés simulés avec les champs correspondants, sinon Mockito pourrait être confus et injection ne se produira pas.

est-ce que cela signifie que si j'ai plusieurs champs avec le même type, Je ne peux pas me moquer D'un seul d'entre eux mais plutôt définir @Mock champs avec le même type? Est-ce une limitation connue et y a-t-il une raison pour laquelle elle n'a pas encore été corrigée? Il devrait être très simple pour correspondre @Mock par les noms des champs, n'est-ce pas?

16
demandé sur Maciej Kowalski 2015-04-29 21:52:37

2 réponses

il semble que Mockito utilise un algorithme décrit dans leur JavaDoc

si je comprends bien, il va d'abord trier sur type (dans ce cas seulement 1 B) et ensuite trier sur Nom (Aucun changement ici). Il va enfin injecter en utilisant le OngoingInjector l'implémentation de l'interface, qui semble chercher le premier champ et l'injecter.

puisque vous avez seulement 1 B défini et il y a 2 champs de B dans la maquette, il verra la correspondance de en première instance sur le terrain et arrêter. C'est parce que mocks.size() == 1 NameBasedCandidateFilter . Par conséquent, il cessera de filtrer et l'injectera directement. Si vous créez plusieurs mocks du même type, ils seront triés sur le nom et injectés en conséquence.

j'ai pu le faire fonctionner quand j'ai créé plusieurs mocks (mais moins que le nombre de champs) d'un type spécifique.

@RunWith(MockitoJUnitRunner.class)
public class MockitoTest {

    @Mock(name = "b2")
    private B b2;

    @Mock(name = "b3")
    private B b3;

    @InjectMocks
    private A a;

    @Test
    public void testInjection() {
        System.out.println(this.a);
    }

    static class A {

        private B b1;

        private B b2;

        private B b3;
    }

    interface B {
    }
}

ceci injectera correctement b2 dans A. b2 et b3 en a. b3 au lieu de A. b1 et A. b2 (les 2 premiers champs définis en A).

Vous pouvez toujours laisser un GitHub question sur leur dépôt avec une amélioration ou un changement sur l'injection algorithme de filtrage afin d'être regardé.

19
répondu Tim van der Lippe 2015-04-29 22:21:32

ceci est documenté dans mockito en tant que travail autour, si plusieurs moqueries existent du même type. Il ne résout pas la mise en œuvre basée sur le nom fourni (c.-à-d. @Mock(name = "b2")). L'algorithme utilisé pour résoudre l'implémentation est par nom de champ de la dépendance injectée. Ainsi votre code ci-dessus résoudra correctement (b2=>@Mock private B b2 et b3=>@Mock private B b3).

L'autre solution consiste à utiliser constructeur injection, qui est la méthode recommandée pour l'injection dépendance.

0
répondu alltej 2017-05-31 13:29:24