Je voudrais injecter un objet maquette Mockito dans un bean Spring (3+) à des fins de tests unitaires avec JUnit. Mes dépendances de bean sont actuellement injectées en utilisant l' @Autowired
annotation sur les champs membres privés.
J'ai envisagé d'utiliser ReflectionTestUtils.setField
mais l'instance de bean que je souhaite injecter est en fait un proxy et ne déclare donc pas les champs membres privés de la classe cible. Je ne souhaite pas créer un setter public pour la dépendance car je modifierai alors mon interface uniquement à des fins de test.
J'ai suivi quelques conseils donnés par la communauté Spring, mais la maquette n'est pas créée et le câblage automatique échoue:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
L'erreur que je rencontre actuellement est la suivante:
...
Caused by: org...NoSuchBeanDefinitionException:
No matching bean of type [com.package.Dao] found for dependency:
expected at least 1 bean which qualifies as autowire candidate for this dependency.
Dependency annotations: {
@org...Autowired(required=true),
@org...Qualifier(value=dao)
}
at org...DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(D...y.java:901)
at org...DefaultListableBeanFactory.doResolveDependency(D...y.java:770)
Si je définit la constructor-arg
valeur sur quelque chose de non valide, aucune erreur ne se produit lors du démarrage du contexte d'application.
Réponses:
La meilleure façon est:
Mettre à jour
Dans le fichier de contexte, cette maquette doit être répertoriée avant tout champ autowired selon qu'elle est déclarée.
la source
Cela injectera tous les objets simulés dans la classe de test. Dans ce cas, il injectera mockedObject dans testObject. Cela a été mentionné ci-dessus mais voici le code.
la source
mockedObject
?Mockito.spy(...)
à lamockedObject
place? Et puis utilisezwhen(mockedObject.execute).thenReturn(objToReturn)
oudoReturn(objToReturn).when(mockedObject).execute()
. La seconde n'invoque pas la vraie méthode. Vous pouvez également consulter laMockito.doCallRealMethod()
documentationJ'ai une solution très simple utilisant Spring Java Config et Mockito:
la source
initMocks
? Pourquoi ne pas simplementreturn Mockito.mock(BeanA.class)
engetBeanA
? De cette façon, c'est plus simple et il y a moins de code. Qu'est-ce que je rate?Donné:
Vous pouvez charger la classe testée via le câblage automatique, simuler la dépendance avec Mockito, puis utiliser ReflectionTestUtils de Spring pour injecter la simulation dans la classe testée.
Veuillez noter qu'avant Spring 4.3.1, cette méthode ne fonctionnera pas avec les services derrière un proxy (annoté avec
@Transactional
ouCacheable
, par exemple). Cela a été corrigé par SPR-14050 .Pour les versions antérieures, une solution consiste à déballer le proxy, comme décrit ici: l' annotation transactionnelle évite de se moquer des services (ce qui se
ReflectionTestUtils.setField
fait par défaut maintenant)la source
Si vous utilisez Spring Boot 1.4, il a une manière impressionnante de le faire. Utilisez simplement une nouvelle marque
@SpringBootTest
sur votre classe et@MockBean
sur le terrain et Spring Boot créera une maquette de ce type et l'injectera dans le contexte (au lieu d'injecter l'original):En revanche, si vous n'utilisez pas Spring Boot ou utilisez une version précédente, vous devrez faire un peu plus de travail:
Créez un
@Configuration
bean qui injecte vos mocks dans le contexte Spring:En utilisant
@Primary
annotation, vous dites au printemps que ce bean a la priorité si aucun qualificatif n'est spécifié.Assurez-vous d'annoter la classe avec
@Profile("useMocks")
afin de contrôler quelles classes utiliseront la maquette et lesquelles utiliseront le vrai bean.Enfin, dans votre test, activez le
userMocks
profil:Si vous ne voulez pas utiliser la maquette mais le vrai bean, n'activez pas le
useMocks
profil:la source
web.xml
configuration old-school et AnnotationConfigWebApplicationContext. J'ai dû utiliser à la@WebAppConfiguration
place de@WebIntegrationTest
et@ContextHierarchy
avec@ContextConfiguration
au lieu de@SpringApplicationConfiguration
.@Primary
annotation pour mon cas, car il y avait un appel qui échouait dans un@PostConstruct
que je voulais simuler, mais le@PostConstruct
bean a été créé avant ma maquette, donc il n'a pas utilisé la maquette (jusqu'à ce que j'ajoute@Primary
).Depuis 1.8.3 Mockito a
@InjectMocks
- c'est incroyablement utile. Mes tests JUnit sont@RunWith
les objetsMockitoJUnitRunner
et je construis@Mock
qui satisfont toutes les dépendances de la classe testée, qui sont tous injectés lorsque le membre privé est annoté avec@InjectMocks
.Je
@RunWith
leSpringJUnit4Runner
pour les tests d'intégration seulement maintenant.Je noterai qu'il ne semble pas pouvoir s'injecter
List<T>
de la même manière que Spring. Il recherche uniquement un objet Mock qui satisfait àList
, et n'injectera pas une liste d'objets Mock. La solution de contournement pour moi consistait à utiliser une@Spy
contre une liste instanciée manuellement et à ajouter manuellement le ou les objets fictifs à cette liste pour les tests unitaires. Peut-être que c'était intentionnel, car cela m'a certainement forcé à porter une attention particulière à ce qui se moquait ensemble.la source
Mettre à jour: il existe désormais de meilleures solutions plus propres à ce problème. Veuillez d'abord considérer les autres réponses.
J'ai finalement trouvé une réponse à cette question par Ronen sur son blog. Le problème que je rencontrais est dû à la méthode
Mockito.mock(Class c)
déclarant un type de retour deObject
. Par conséquent, Spring n'est pas en mesure de déduire le type de bean du type de retour de méthode d'usine.La solution de Ronen consiste à créer une
FactoryBean
implémentation qui renvoie des simulations. leFactoryBean
interface permet à Spring d'interroger le type d'objets créés par le bean d'usine.Ma définition de bean simulé ressemble maintenant à:
la source
Depuis le printemps 3.2, ce n'est plus un problème. Spring prend désormais en charge le câblage automatique des résultats des méthodes d'usine génériques. Voir la section intitulée "Méthodes génériques d'usine" dans cet article de blog: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .
Le point clé est:
Ce qui signifie que cela devrait fonctionner hors de la boîte:
la source
Le code ci-dessous fonctionne avec le câblage automatique - ce n'est pas la version la plus courte mais utile quand il ne devrait fonctionner qu'avec des pots à ressort / mockito standard.
la source
Si vous utilisez spring> = 3.0 , essayez d'utiliser l'
@Configuration
annotation Springs pour définir une partie du contexte de l'applicationSi vous ne souhaitez pas utiliser @ImportResource, cela peut aussi être fait dans l'autre sens:
Pour plus d'informations, consultez Spring-Framework-Reference: configuration de conteneur basée sur Java
la source
Ce n'est peut-être pas la solution parfaite, mais j'ai tendance à ne pas utiliser le ressort pour faire DI pour les tests unitaires. les dépendances pour un seul bean (la classe sous test) ne sont généralement pas trop complexes donc je fais juste l'injection directement dans le code de test.
la source
Je peux faire ce qui suit en utilisant Mockito:
la source
Affichage de quelques exemples basés sur les approches ci-dessus
Avec le printemps:
Sans ressort:
la source
Mise à jour - nouvelle réponse ici: https://stackoverflow.com/a/19454282/411229 . Cette réponse ne s'applique qu'à ceux des versions Spring antérieures à 3.2.
J'ai cherché pendant un certain temps une solution plus définitive à cela. Ce billet de blog semble couvrir tous mes besoins et ne repose pas sur la commande de déclarations de bean. Tous les crédits à Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
Fondamentalement, implémentez un FactoryBean
Mettez à jour votre configuration de printemps avec les éléments suivants:
la source
En regardant le rythme de développement de Springockito et le nombre de problèmes en suspens , je serais un peu inquiet de l'introduire dans ma pile de suite de tests de nos jours. Le fait que la dernière version ait été réalisée avant la version Spring 4 soulève des questions comme "Est-il possible de l'intégrer facilement à Spring 4?". Je ne sais pas, parce que je ne l'ai pas essayé. Je préfère l'approche Spring pure si j'ai besoin de se moquer de Spring Bean dans le test d'intégration.
Il existe une option pour simuler le haricot de printemps avec des fonctionnalités de printemps simples. Vous devez l'utiliser
@Primary
,@Profile
et des@ActiveProfiles
annotations pour cela. J'ai écrit un article de blog sur le sujet.la source
J'ai trouvé une réponse similaire à celle du teabot pour créer une MockFactory qui fournit les simulacres. J'ai utilisé l'exemple suivant pour créer la fausse usine (car le lien vers narkisr est mort): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
Cela permet également d'éviter que Spring veuille résoudre les injections du haricot moqué.
la source
cela ^ fonctionne parfaitement bien s'il est déclaré en premier / au début du fichier XML. Mockito 1.9.0 / Spring 3.0.5
la source
J'utilise une combinaison de l'approche utilisée en réponse par Markus T et une implémentation d'aide simple
ImportBeanDefinitionRegistrar
qui recherche une annotation personnalisée (@MockedBeans
) dans laquelle on peut spécifier quelles classes doivent être moquées. Je crois que cette approche se traduit par un test unitaire concis avec une partie du code passe-partout lié à la moquerie supprimée.Voici à quoi ressemble un exemple de test unitaire avec cette approche:
Pour ce faire, vous devez définir deux classes d'assistance simples - une annotation personnalisée (
@MockedBeans
) et uneImportBeanDefinitionRegistrar
implémentation personnalisée .@MockedBeans
la définition d'annotation doit être annotée@Import(CustomImportBeanDefinitionRegistrar.class)
et laImportBeanDefinitionRgistrar
nécessité d'ajouter des définitions de beans fictifs à la configuration dans saregisterBeanDefinitions
méthode.Si vous aimez l'approche, vous pouvez trouver des exemples d' implémentations sur mon blogpost .
la source
J'ai développé une solution basée sur la proposition de Kresimir Nesek. J'ai ajouté une nouvelle annotation @EnableMockedBean afin de rendre le code un peu plus propre et modulaire.
J'ai écrit un article pour l' expliquer.
la source
Je suggère de migrer votre projet vers Spring Boot 1.4. Après cela, vous pouvez utiliser une nouvelle annotation
@MockBean
pour simuler votrecom.package.Dao
la source
Aujourd'hui, j'ai découvert qu'un contexte printanier où je déclarais un avant les haricots Mockito ne se chargeait pas. Après avoir déplacé APRÈS les simulations, le contexte de l'application a été chargé avec succès. Prends soin de toi :)
la source
Pour mémoire, tous mes tests fonctionnent correctement en faisant juste l'initialisation paresseux du projecteur, par exemple:
Je suppose que la justification est celle que Mattias explique ici (au bas du message), qu'une solution de contournement change l'ordre dans lequel les beans sont déclarés - l'initialisation paresseuse est une sorte de "déclaration" du luminaire à la fin.
la source
Si vous utilisez l'injection de contrôleur, assurez-vous que vos variables locales ne sont PAS "finales"
la source