Me gustaría inyectar un objeto simulado Mockito en un bean Spring (3+) para realizar pruebas unitarias con JUnit. Actualmente, mis dependencias de bean se inyectan mediante la @Autowired
anotación en campos de miembros privados.
He considerado usar, ReflectionTestUtils.setField
pero la instancia de bean que deseo inyectar es en realidad un proxy y, por lo tanto, no declara los campos de miembros privados de la clase de destino. No deseo crear un setter público para la dependencia, ya que luego modificaré mi interfaz con el único fin de realizar pruebas.
He seguido algunos consejos dados por la comunidad de Spring, pero el simulacro no se crea y el cableado automático falla:
<bean id="dao" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.package.Dao" />
</bean>
El error que encuentro actualmente es el siguiente:
...
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 configuro el constructor-arg
valor en algo no válido, no se produce ningún error al iniciar el contexto de la aplicación.
Respuestas:
La mejor manera es:
Actualización
En el archivo de contexto, este simulacro debe aparecer antes de que se declare cualquier campo con conexión automática, según se declare.
fuente
Esto inyectará cualquier objeto burlado en la clase de prueba. En este caso, inyectará mockedObject en testObject. Esto se mencionó anteriormente, pero aquí está el código.
fuente
mockedObject
?Mockito.spy(...)
esto en sumockedObject
lugar? Y luego usawhen(mockedObject.execute).thenReturn(objToReturn)
odoReturn(objToReturn).when(mockedObject).execute()
. El segundo no invoca el método real. También puede consultar laMockito.doCallRealMethod()
documentaciónTengo una solución muy simple usando Spring Java Config y Mockito:
fuente
initMocks
? ¿Por qué no soloreturn Mockito.mock(BeanA.class)
adentrogetBeanA
? De esta manera es más simple y hay menos código. ¿Qué me estoy perdiendo?Dado:
Puede cargar la clase que se está probando mediante el cableado automático, simular la dependencia con Mockito y luego usar Spring's ReflectionTestUtils para inyectar el simulacro en la clase que se está probando.
Tenga en cuenta que antes de Spring 4.3.1, este método no funcionará con servicios detrás de un proxy (anotado con
@Transactional
, oCacheable
, por ejemplo). Esto ha sido arreglado por SPR-14050 .Para versiones anteriores, una solución es desenvolver el proxy, como se describe allí: la anotación transaccional evita la burla de los servicios (que es lo que
ReflectionTestUtils.setField
hace ahora por defecto)fuente
Si está utilizando Spring Boot 1.4, tiene una forma increíble de hacerlo. Simplemente use una nueva marca
@SpringBootTest
en su clase y@MockBean
en el campo y Spring Boot creará un simulacro de este tipo y lo inyectará en el contexto (en lugar de inyectar el original):Por otro lado, si no está utilizando Spring Boot o está utilizando una versión anterior, tendrá que trabajar un poco más:
Crea un
@Configuration
bean que inyecte tus simulacros en el contexto de Spring:Usando la
@Primary
anotación, le está diciendo a Spring que este bean tiene prioridad si no se especifica ningún calificador.Asegúrese de anotar la clase con el
@Profile("useMocks")
fin de controlar qué clases usarán el simulacro y cuáles usarán el bean real.Finalmente, en su prueba, active el
userMocks
perfil:Si no desea utilizar el simulacro sino el bean real, simplemente no active el
useMocks
perfil:fuente
web.xml
configuración de la vieja escuela y AnnotationConfigWebApplicationContext. Tuve que usar en@WebAppConfiguration
lugar de@WebIntegrationTest
y@ContextHierarchy
con en@ContextConfiguration
lugar de@SpringApplicationConfiguration
.@Primary
anotación para mi caso, ya que hubo una llamada fallida dentro de una@PostConstruct
que quería burlarme, pero el@PostConstruct
bean 's se creó antes de mi simulación para que no lo usara (hasta que agregué@Primary
).Desde 1.8.3 Mockito lo ha hecho
@InjectMocks
, esto es increíblemente útil. Mis pruebas JUnit son@RunWith
elMockitoJUnitRunner
y construir@Mock
objetos que satisfacen todas las dependencias de la clase siendo probados, los cuales están inyectados cuando el miembro privado se anota con@InjectMocks
.Yo
@RunWith
laSpringJUnit4Runner
de las pruebas de integración sólo ahora.Notaré que no parece ser capaz de inyectar
List<T>
de la misma manera que Spring. Solo busca un objeto Mock que satisfaga elList
, y no inyectará una lista de objetos Mock. La solución para mí fue utilizar una@Spy
lista de instancia manual en contra y agregar manualmente los objetos simulados a esa lista para pruebas unitarias. Tal vez eso fue intencional, porque ciertamente me obligó a prestar mucha atención a lo que se estaba burlando juntos.fuente
Actualización: ahora hay soluciones mejores y más limpias para este problema. Por favor considere las otras respuestas primero.
Finalmente encontré una respuesta a esto por parte de ronen en su blog. El problema que estaba teniendo se debe al método que
Mockito.mock(Class c)
declara un tipo de retorno deObject
. En consecuencia, Spring no puede inferir el tipo de bean del tipo de retorno del método de fábrica.La solución de Ronen es crear una
FactoryBean
implementación que devuelva simulacros. LaFactoryBean
interfaz permite a Spring consultar el tipo de objetos creados por el bean de fábrica.Mi definición de frijol burlado ahora se ve así:
fuente
A partir de la primavera 3.2, esto ya no es un problema. Spring ahora admite el Autowiring de los resultados de los métodos genéricos de fábrica. Consulte la sección titulada "Métodos genéricos de fábrica" en esta publicación de blog: http://spring.io/blog/2012/11/07/spring-framework-3-2-rc1-new-testing-features/ .
El punto clave es:
Lo que significa que esto debería funcionar fuera de la caja:
fuente
El siguiente código funciona con el cableado automático: no es la versión más corta pero es útil cuando debería funcionar solo con frascos de resorte / mockito estándar.
fuente
Si está usando spring> = 3.0 , intente usar la
@Configuration
anotación Springs para definir parte del contexto de la aplicaciónSi no desea utilizar @ImportResource, también puede hacerlo al revés:
Para obtener más información, eche un vistazo a spring-framework-reference: configuración de contenedor basada en Java
fuente
Quizás no sea la solución perfecta, pero tiendo a no usar el resorte para hacer DI para las pruebas unitarias. las dependencias para un solo bean (la clase bajo prueba) generalmente no son demasiado complejas, así que solo hago la inyección directamente en el código de prueba.
fuente
Puedo hacer lo siguiente usando Mockito:
fuente
Publicar algunos ejemplos basados en los enfoques anteriores
Con la primavera:
Sin primavera:
fuente
Actualización : nueva respuesta aquí: https://stackoverflow.com/a/19454282/411229 . Esta respuesta solo se aplica a las versiones Spring anteriores a la 3.2.
He buscado por un tiempo una solución más definitiva para esto. Esta publicación de blog parece cubrir todas mis necesidades y no se basa en el pedido de declaraciones de frijol. Todo el crédito a Mattias Severson. http://www.jayway.com/2011/11/30/spring-integration-tests-part-i-creating-mock-objects/
Básicamente, implemente FactoryBean
A continuación, actualice su configuración de primavera con lo siguiente:
fuente
Mirando el ritmo de desarrollo de Springockito y la cantidad de problemas abiertos , me preocuparía un poco introducirlo en mi paquete de pruebas hoy en día. El hecho de que el último lanzamiento se realizó antes del lanzamiento de Spring 4 plantea preguntas como "¿Es posible integrarlo fácilmente con Spring 4?". No lo sé porque no lo intenté. Prefiero el enfoque Spring puro si necesito burlarme de Spring Bean en la prueba de integración.
Hay una opción para falsificar Spring Bean con características simples de Spring. Necesita usar
@Primary
,@Profile
y@ActiveProfiles
anotaciones para ello. Escribí una publicación de blog sobre el tema.fuente
Encontré una respuesta similar a la de teabot para crear un MockFactory que proporciona los simulacros. Usé el siguiente ejemplo para crear la fábrica simulada (ya que el enlace a narkisr está muerto): http://hg.randompage.org/java/src/407e78aa08a0/projects/bookmarking/backend/spring/src/test/java/ org / randompage / bookmarking / backend / testUtils / MocksFactory.java
Esto también ayuda a evitar que Spring quiera resolver las inyecciones del frijol burlado.
fuente
esto ^ funciona perfectamente bien si se declara primero / temprano en el archivo XML. Mockito 1.9.0 / Spring 3.0.5
fuente
Utilizo una combinación del enfoque utilizado en la respuesta por Markus T y una implementación auxiliar simple
ImportBeanDefinitionRegistrar
que busca una anotación personalizada (@MockedBeans
) en la que se puede especificar qué clases se deben burlar. Creo que este enfoque da como resultado una prueba de unidad concisa con parte del código repetitivo relacionado con la burla eliminada.Así es como se ve una prueba de unidad de muestra con ese enfoque:
Para que esto suceda, debe definir dos clases auxiliares simples: anotación personalizada (
@MockedBeans
) y unaImportBeanDefinitionRegistrar
implementación personalizada .@MockedBeans
la definición de anotación debe ser anotada@Import(CustomImportBeanDefinitionRegistrar.class)
y laImportBeanDefinitionRgistrar
necesidad de agregar definiciones de beans simulados a la configuración en suregisterBeanDefinitions
método.Si le gusta el enfoque, puede encontrar implementaciones de muestra en mi blog .
fuente
Desarrollé una solución basada en la propuesta de Kresimir Nesek. Agregué una nueva anotación @EnableMockedBean para hacer que el código sea un poco más limpio y modular.
He escrito un post explicándolo.
fuente
Sugeriría migrar su proyecto a Spring Boot 1.4. Después de eso, puede usar una nueva anotación
@MockBean
para falsificar sucom.package.Dao
fuente
Hoy descubrí que un contexto primaveral donde declaraba un antes de los frijoles Mockito, no se cargaba. Después de mover DESPUÉS de los simulacros, el contexto de la aplicación se cargó correctamente. Cuídate :)
fuente
Para el registro, todas mis pruebas funcionan correctamente simplemente haciendo que el dispositivo se inicialice lentamente, por ejemplo:
Supongo que la razón es la que Mattias explica aquí (al final de la publicación), que una solución alternativa está cambiando el orden en que se declaran los beans: la inicialización diferida es "una especie de" tener el dispositivo declarado al final.
fuente
Si usa la inyección del controlador, asegúrese de que sus variables locales NO sean "finales"
fuente