web-dev-qa-db-fra.com

Injection d'une propriété String avec @InjectMocks

J'ai un Spring MVC @Controller avec ce constructeur:

@Autowired
public AbcController(XyzService xyzService, @Value("${my.property}") String myProperty) {/*...*/}

Je veux écrire un test unitaire autonome pour ce contrôleur:

@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {

    @Mock
    private XyzService mockXyzService;

    private String myProperty = "my property value";

    @InjectMocks
    private AbcController controllerUnderTest;

    /* tests */
}

Existe-t-il un moyen d'obtenir @InjectMocks pour injecter ma propriété String? Je sais que je ne peux pas me moquer d'une chaîne car elle est immuable, mais puis-je simplement injecter une chaîne normale ici?

@InjectMocks injecte un null par défaut dans ce cas. @Mock lève naturellement une exception si je la mets sur myProperty. Y a-t-il une autre annotation que j'ai manquée qui signifie simplement "injecter cet objet exact plutôt qu'une maquette"?

22
Sam Jones

Vous ne pouvez pas faire cela avec Mockito, car, comme vous l'avez mentionné vous-même, un String est final et ne peut pas être moqué.

Il y a un @Spy annotation qui fonctionne sur objets réels , mais elle a les mêmes limitations que @Mock, vous ne pouvez donc pas espionner un String.

Il n'y a aucune annotation pour dire à Mockito de simplement injecter cette valeur sans se moquer ni espionner. Ce serait une bonne fonctionnalité, cependant. Peut-être le suggérer au référentiel Mockito Github .

Vous devrez instancier manuellement votre contrôleur si vous ne voulez pas changer votre code.

La seule façon d'avoir un test basé sur une annotation pure est de refactoriser le contrôleur. Il peut utiliser un objet personnalisé qui ne contient qu'une seule propriété, ou peut-être une classe de configuration avec plusieurs propriétés.

@Component
public class MyProperty {

    @Value("${my.property}")
    private String myProperty;

    ...
}

Cela peut être injecté dans le contrôleur.

@Autowired
public AbcController(XyzService xyzService, MyProperty myProperty) { 
    ... 
}

Vous pouvez alors vous moquer et injecter cela.

@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {

    @Mock
    private XyzService mockXyzService;

    @Mock
    private MyProperty myProperty;

    @InjectMocks
    private AbcController controllerUnderTest;

    @Before
    public void setUp(){
        when(myProperty.get()).thenReturn("my property value");
    }

    /* tests */
}

Ce n'est pas assez simple, mais au moins vous pourrez avoir un test basé sur des annotations pures avec un peu de stubbing.

8
Tom Verelst

Vous ne pouvez pas faire cela avec Mockito, mais Apache Commons a en fait un moyen de le faire en utilisant l'un de ses utilitaires intégrés. Vous pouvez mettre cela dans une fonction dans JUnit qui est exécutée après que Mockito a injecté le reste des mocks mais avant l'exécution de vos cas de test, comme ceci:

@InjectMocks
MyClass myClass;

@Before
public void before() throws Exception {
    FieldUtils.writeField(myClass, "fieldName", fieldValue, true);
}
24
Jacob Beasley

N'utilisez simplement pas @InjectMocks dans ce cas.

faire:

@Before
public void setup() {
 controllerUnderTest = new AbcController(mockXyzService, "my property value");
}
2
Jan Groot

Puisque vous utilisez Spring, vous pouvez utiliser le org.springframework.test.util.ReflectionTestUtils du spring-test module. Il enveloppe parfaitement la définition d'un champ sur un objet ou d'un champ statique sur une classe (avec d'autres méthodes utilitaires).

@RunWith(MockitoJUnitRunner.class)
public class AbcControllerTest {

    @Mock
    private XyzService mockXyzService;

    @InjectMocks
    private AbcController controllerUnderTest;

    @Before
    public void setUp(){
        ReflectionTestUtils.setField(controllerUnderTest, "myProperty", 
               "String you want to inject");
    }

    /* tests */
}
2
jebeaudet

Ce que vous pouvez utiliser est le suivant: org.mockito.internal.util.reflection.Whitebox

  1. Refonte de votre constructeur de classe "AbcController"
  2. Dans votre méthode "avant" de la classe Test, utilisez la méthode Whitebox.setInternalState pour spécifier la chaîne souhaitée

    @Before
    public void setUp(){
        Whitebox.setInternalState(controllerUnderTest, "myProperty", "The string that you want"); }
    
0
Abhinav Ganguly