mvc form spring-mvc mockito spring-test

spring mvc - form - Cómo limpiar simulacros en las pruebas de primavera al usar Mockito



spring form input (4)

  1. Acerca de colocar el reinicio después del método de prueba

    Creo que es mejor reiniciar los simulacros después del método de prueba, ya que implica que hay algo que sucedió durante la prueba que debe limpiarse.

    Si el restablecimiento se realiza antes del método de prueba, me sentiría inseguro, ¿qué diablos sucedió antes de la prueba que se debe restablecer? ¿Qué pasa con el objeto no simulacros? ¿Hay una razón (tal vez la hay) para ello? Si hay una razón por la que no se menciona en el código (por ejemplo, el nombre del método)? Etcétera.

  2. No soy un fan de las pruebas basadas en primavera

    1. Fondo

      Usar Spring es como renunciar a la unidad de pruebas de una clase; con Spring, tiene menos control sobre la prueba: aislamiento , creación de instancias , ciclo de vida , para citar algunas de las propiedades buscadas en una prueba de unidad. Sin embargo, en muchos casos, Spring ofrece bibliotecas y marcos que no son tan " transparentes ", para probar que es mejor probar el comportamiento real de todo el material, como con Spring MVC, Spring Batch, etc.

      Y la elaboración de estas pruebas es mucho más problemática, ya que en muchos casos impone que el desarrollador elabore pruebas de integración para probar seriamente el comportamiento del código de producción. Como muchos desarrolladores no conocen cada detalle sobre cómo vive su código dentro de Spring, esto puede llevar a muchas sorpresas al intentar probar una clase con pruebas de unidad.

      Pero los problemas continúan, las pruebas deben ser rápidas y pequeñas para dar retroalimentación rápida a los desarrolladores (el complemento IDE como Infinitest son excelentes para eso), pero las pruebas con Spring son intrínsecamente más lentas y consumen más memoria. Lo que tiende a ejecutarlos con menos frecuencia e incluso evitarlos totalmente en la estación de trabajo local ... para descubrir más tarde en el servidor CI que fallan.

    2. Ciclo de vida con mockito y primavera.

      Entonces, cuando se crea una prueba de integración para un subsistema, terminas con muchos objetos y, obviamente, con los colaboradores, que probablemente se burlan. El ciclo de vida está controlado por Spring Runner, pero Mockito no lo está. Así que tienes que gestionar el ciclo de vida de los mocks tú mismo.

      Nuevamente sobre el ciclo de vida durante un proyecto con Spring Batch tuvimos algunos problemas con los efectos residuales en los simulacros, por lo que tuvimos dos opciones, hicimos solo un método de prueba por clase de prueba o usamos el truco de contexto sucio : @DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD) . Esto condujo a pruebas más lentas, mayor consumo de memoria, pero fue la mejor opción que teníamos. Con este truco no tendrás que reiniciar las burlas de Mockito.

  3. Una posible luz en la oscuridad.

    No conozco bien el proyecto, pero springockito puede proporcionarle algo de azúcar sobre el ciclo de vida. El subproyecto de anotación parece ser aún mejor: parece permitir que Spring administre el ciclo de vida de los beans en el contenedor de Spring y permitir que la prueba controle cómo se utilizan los simulacros. Todavía no tengo experiencia con esta herramienta, por lo que puede haber sorpresas.

Como un descargo de responsabilidad, me gusta mucho Spring, ofrece muchas herramientas notables para simplificar el uso de otros frameworks, puede aumentar la productividad, ayuda al diseño, pero como todas las herramientas que inventaron los Humanos, siempre hay una ventaja (si no más ... ).

En una nota al margen, es interesante ver que esta problemática está en un contexto JUnit , ya que JUnit crea una instancia de la clase de prueba para cada método de prueba. Si la prueba se basara en TestNG, entonces el enfoque podría ser un poco diferente, ya que TestNG crea solo una instancia de la clase de prueba, los campos simulados en reposo serían obligatorios independientemente del uso de Spring.

Respuesta antigua:

No soy un gran fanático de usar Mockito en un texto de primavera. Pero podrías estar buscando algo como:

@After public void reset_mocks() { Mockito.reset(placeOrderService); }

Soy bastante nuevo en Mockito y tengo algunos problemas con la limpieza.

Solía ​​usar JMock2 para pruebas unitarias. Por lo que sé, JMock2 conserva las expectativas y otra información simulada en un contexto que se reconstruirá para cada método de prueba. Por lo tanto, cada método de prueba no es interferido por los otros.

Adopté la misma estrategia para las pruebas de primavera al utilizar JMock2. Encontré un problema potencial con las estrategias que usé en mi post : el contexto de la aplicación se reconstruye para cada método de prueba y, por lo tanto, ralentiza todo el procedimiento de prueba.

Noté que muchos artículos recomiendan el uso de Mockito en las pruebas de primavera y me gustaría intentarlo. Funciona bien hasta que escribo dos métodos de prueba en un caso de prueba. Cada método de prueba pasó cuando se ejecutó solo, uno de ellos falló si se ejecutaron juntos. Especulé que esto se debe a que la información simulada se conservó en la simulación en sí misma (porque no veo ningún objeto de contexto como el de JMock) y la simulación (y el contexto de la aplicación) se comparte en ambos métodos de prueba.

Lo resolví agregando reset () en el método @Before. Mi pregunta es ¿cuál es la mejor práctica para manejar esta situación (el javadoc de reset () dice que el código es olor si necesitas reset ())? Cualquier idea es de agradecer, gracias de antemano.

@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/booking-servlet.xml", "classpath:test-booking-servlet.xml" }) @WebAppConfiguration public class PlaceOrderControllerIntegrationTests implements IntegrationTests { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Autowired private PlaceOrderService placeOrderService; @Before public void setup() { this.mockMvc = webAppContextSetup(this.wac).build(); reset(placeOrderService);// reset mock } @Test public void fowardsToFoodSelectionViewAfterPendingOrderIsPlaced() throws Exception { final Address deliveryAddress = new AddressFixture().build(); final String deliveryTime = twoHoursLater(); final PendingOrder pendingOrder = new PendingOrderFixture() .with(deliveryAddress).at(with(deliveryTime)).build(); when(placeOrderService.placeOrder(deliveryAddress, with(deliveryTime))) .thenReturn(pendingOrder); mockMvc.perform(...); } @Test public void returnsToPlaceOrderViewWhenFailsToPlaceOrder() throws Exception { final Address deliveryAddress = new AddressFixture().build(); final String deliveryTime = twoHoursLater(); final PendingOrder pendingOrder = new PendingOrderFixture() .with(deliveryAddress).at(with(deliveryTime)).build(); NoAvailableRestaurantException noAvailableRestaurantException = new NoAvailableRestaurantException( deliveryAddress, with(deliveryTime)); when(placeOrderService.placeOrder(deliveryAddress, with(deliveryTime))) .thenThrow(noAvailableRestaurantException); mockMvc.perform(...); }


En lugar de inyectar el objeto placeOrderService , probablemente debería dejar que Mockito lo inicialice como @Mock antes de cada prueba, a través de algo como esto:

@Mock private PlaceOrderService placeOrderService; @Before public void setup() { MockitoAnnotations.initMocks(this); }

Como se recomienda en el Javadoc aquí: http://docs.mockito.googlecode.com/hg/latest/org/mockito/MockitoAnnotations.html

Incluso puede poner el método @Before en una superclase y simplemente extenderlo para cada clase de caso de prueba que use objetos @Mock .


La prueba basada en la primavera es difícil de hacer rápida e indepen- dientemente (como wrote @Brice). Aquí hay un método de utilidad litle para restablecer todos los simulacros (debe llamarlo manualmente en cada método @Before ):

import org.mockito.Mockito; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.springframework.context.ApplicationContext; public class MyTest { public void resetAll(ApplicationContext applicationContext) throws Exception { for (String name : applicationContext.getBeanDefinitionNames()) { Object bean = applicationContext.getBean(name); if (AopUtils.isAopProxy(bean) && bean instanceof Advised) { bean = ((Advised)bean).getTargetSource().getTarget(); } if (Mockito.mockingDetails(bean).isMock()) { Mockito.reset(bean); } } } }

Como ve, hay una iteración para todos los beans, verifique si el bean es simulado o no, y reinicie el simulacro. AopUtils.isAopProxy especial atención a las llamadas AopUtils.isAopProxy y ((Advised)bean).getTargetSource().getTarget() . Si el bean contiene una anotación @Transactional el simulacro de este bean siempre se envuelve por resorte en un objeto proxy, por lo que para restablecer o verificar este simulacro, primero debe desenvolverlo. De lo contrario, recibirá una UnfinishedVerificationException que puede surgir en diferentes pruebas de vez en cuando.

En mi caso, AopUtils.isAopProxy es suficiente. Pero también hay AopUtils.isCglibProxy y AopUtils.isJdkDynamicProxy si tiene problemas con el proxy.

mockito es 1.10.19 prueba de primavera es 3.2.2.RELEASE