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"?
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.
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);
}
N'utilisez simplement pas @InjectMocks dans ce cas.
faire:
@Before
public void setup() {
controllerUnderTest = new AbcController(mockXyzService, "my property value");
}
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 */
}
Ce que vous pouvez utiliser est le suivant: org.mockito.internal.util.reflection.Whitebox
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"); }