2012-02-10 18 views
5

Soy nuevo en el marco de primavera y tengo una pregunta sobre sus capacidades de inyección de dependencia usando Spring Context.Creando una nueva instancia de un bean después de cada prueba de unidad

Ésta es la clase que estoy tratando de escribir una prueba de integración para:

public class UserService { 

private Validator validator; 
private UserRepository userRepository; 
private Encryptor encryptor; 
private MailService mailService; 

... 

public void registerUser(User user) { 
    user.setPassword(encryptor.encrypt(user.getPassword())); 

    Errors errors = new BindException(user, "user"); 
    validator.validate(user, errors); 

    if (errors.getErrorCount() == 0) { 
     userRepository.addUser(user); 
     mailService.sendMail(user.getEmail()); 
    } 
} 

En mis pruebas (usando Mockito) Quiero asegurar a los cuatro elementos se denominan así que crear pruebas como:

public void testRegisterCallsValidateInValidator() { 
    userService.registerUser(testUser); 
    verify(userService.getValidator(), times(1)).validate(any(User.class), any(Errors.class)); 
} 

Sin embargo, todas las pruebas fallan diciendo que he invocado el método varias veces. Mi única conjetura es que el bean UserService se crea una vez al comienzo de todas las pruebas, pero no se vuelve a cargar después de cada prueba.

En mi configuración de prueba que utilice el siguiente código XML para decidir qué frijoles a inyectar: ​​

<bean id="userService" class="be.kdg.coportio.services.UserService"> 
    <property name="validator" ref="validator"/> 
    <property name="userRepository" ref="userRepository"/> 
    <property name="encryptor" ref="encryptor"/> 
    <property name="mailService" ref="mailService"/> 
</bean> 

¿Alguna idea?

+0

¿tiene varios métodos de prueba, o solo uno que pegó? – ggreiner

+0

Tengo cuatro métodos de prueba (1 de los cuales he pegado). Recibo tres pruebas fallidas que dicen que llamé a los métodos que trato de probar, respectivamente, 2, 3 y 4 veces. – geoffreydv

Respuesta

6

Para Las pruebas unitarias y de integración claramente separados (pasando por alto el debate de lo que significa cada categoría): puede probar su servicio de dos maneras:

  • a través de una prueba de integración: inicia todo el contexto de Spring y prueba el servicio como un bean singleton.
  • a través de una prueba de unidad: simplemente inicia el servicio usted mismo, se burla de lo que se debe burlar, sin necesidad de Spring.

Mi sugerencia es no mezclar la primavera y se burla de si puede evitarlo - mantener Mockito para pruebas unitarias (que es lo que necesita por lo visto) y el uso de pruebas de integración que Bootstrap todo el contexto de primavera para las pruebas otras cosas - problemas de persistencia, transacciones, etc.

No necesita Spring para burlarse de los colaboradores de una clase y hacer pruebas simples de interacción con Mockito.

+1

"Mi sugerencia es no mezclar Spring y burlarse si puedes evitarlo" exactamente. Inyecte sus simulacros manualmente si tiene instaladores o usa el método 'ReflectionTestUtils.setField()' de Spring. No es necesario encender un contexto de primavera si cambian sus servicios para cada prueba –

+0

Su consejo me ayudó a aclarar algunas cosas. Estaba haciendo un uso indebido de las palabras "prueba de integración", mientras que realmente me refería a la prueba de interacción. Originalmente elegí no usar la primavera para la inyección de mis simulacros, así que estoy volviendo a ese código ahora, solo usando la primavera para pruebas de integración reales. ¡Gracias! – geoffreydv

2

en su método @Before, asegúrese de restablecer sus objetos simulados.

@Before 
public void setup(){ 
    Mockito.reset(validator); 
} 
+0

Por algún motivo, una de las pruebas se ha corregido pero las demás aún fallan (el mismo mensaje que antes). He puesto 4 líneas en el método de configuración como esta: Mockito.reset (userService.getValidator()); Tal vez falla porque estoy usando un getter? Solo tengo un atributo en la clase de prueba para UserService, no para los 4 objetos individuales. – geoffreydv

0

Puede intentar llamar a setDirty (verdadero) en su método de prueba para volver a cargar el contexto de primavera.

0

Nunca he usado Mockito, pero los Spring-Beans son Singleton por defecto, por lo que no se volverían a crear a menos que llame al refresh() en Spring-Container.

Si de todos modos no te necesita que sean únicos, usted podría fijar su alcance a prototype que crearía nuevos casos de frijol en cada inyección ...

24

Está reutilizando su contexto, para tener pruebas independientes entre sí, probablemente necesite actualizar su contexto después de cada prueba para restablecer todo.

Supongo que está utilizando Junit 4.5+. Sería similar con otros marcos de prueba.

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations={"mycontext.xml"}) 
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) 
public class MyTestClass { 
... 
    // my tests  
... 
} 

usted puede poner el @DirtiesContext a nivel de método si los métodos que hay que "arreglar" son pocos, pero si está utilizando la primavera su mejor opción es hacerlo después de cada prueba.

De todos modos, no creo que usted debe utilizar burla/espías en las pruebas de integración:

  • En las pruebas de unidad, utilice burla (si lo desea) e inyectar manualmente. Aquí quiere verificar el comportamiento de su frijol probado como una unidad, por lo que lo aislará del resto mediante simulaciones. Esto también tiene la ventaja de que JUnit aísla sus pruebas al usar una instancia diferente de la clase de prueba para cada prueba, por lo tanto, a menos que use static u otras prácticas antipáticas, todo funcionará.

  • En pruebas de integración, use judías reales y deje que Spring se inyecte. Aquí el objetivo es verificar que los beans interactúen bien entre sí/con el entorno (base de datos, red, ...) Hace no desea aislar los beans aquí, por lo que no debería utilizar los simulacros.

Consulte Spring documentation about testing para obtener explicaciones más detalladas.

+0

Gracias por el ejemplo de código. Como ya comenté la respuesta de Eugen, mezclé los dos tipos de pruebas. Esto es lo que usaré en mis pruebas de integración reales :) – geoffreydv

+0

Muchas gracias. Esto también se puede aplicar en TestNG-Testclasses que extienden 'AbstractTestNGSpringContextTests'. –

+0

AFTER_EACH_TEST_METHOD, increíble. Esto realmente ayudó con nuevos objetos de página para cada una de mis pruebas. – Will

Cuestiones relacionadas