Situación y problema : en Spring Boot, ¿cómo puedo inyectar una o más clases/beans simulados en la aplicación para realizar una prueba de integración? Hay algunas respuestas en StackOverflow, pero se centran en la situación anterior a Spring Boot 1.4 o simplemente no funcionan para mí.
El trasfondo es que, en el siguiente código, la implementación de la configuración se basa en servidores de terceros y otros sistemas externos. La funcionalidad de Configuración ya se probó en una prueba de unidad, por lo que para una prueba de integración completa, quiero simular la dependencia de estos servidores o sistema y solo proporcionar valores ficticios.
MockBean ignorará todas las definiciones de beans existentes y proporcionará un objeto ficticio, pero este objeto no proporciona un comportamiento de método en otras clases que inyectan esta clase. El uso de la forma @Before para establecer el comportamiento antes de una prueba no influye en el objeto inyectado o no se inyecta en otros servicios de aplicación como AuthenticationService.
Mi pregunta : ¿Cómo puedo inyectar mis beans en el contexto de la aplicación? Mi prueba:
package ch.swaechter.testapp; import ch.swaechter.testapp.utils.settings.Settings; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.test.context.junit4.SpringRunner; @TestConfiguration @RunWith(SpringRunner.class) @SpringBootTest(classes = {MyApplication.class}) public class MyApplicationTests { @MockBean private Settings settings; @Before public void before() { Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret"); } @Test public void contextLoads() { String applicationsecret = settings.getApplicationSecret(); System.out.println("Application secret: " + applicationsecret); } }Y debajo un servicio que debería usar la clase simulada, pero no recibe esta clase simulada:
package ch.swaechter.testapp; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class AuthenticationServiceImpl implements AuthenticationService { private final Settings settings; @Autowired public AuthenticationServiceImpl(Settings settings) { this.settings = settings; } @Override public boolean loginUser(String token) { // Use the application secret to check the token signature // But here settings.getApplicationSecret() will return null (Instead of Application Secret as specified in the mock)! return false; } }Parece que está utilizando el objeto Configuración antes de especificar su comportamiento simulado. tienes que correr
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");durante la instalación de la configuración. Para evitar eso, puede crear una clase de configuración especial solo para prueba.
@RunWith(SpringRunner.class) @SpringBootTest(classes = {MyApplication.class, MyApplicationTest.TestConfig.class}) public class MyApplicationTest { private static final String SECRET = "Application Secret"; @TestConfiguration public static class TestConfig { @Bean @Primary public Settings settingsBean(){ Settings settings = Mockito.mock(Settings.class); Mockito.when(settings.getApplicationSecret()).thenReturn(SECRET); Mockito.doReturn(SECRET).when(settings).getApplicationSecret(); return settings; } } ..... }También te recomendaría usar la siguiente notación para burlarte:
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();No ejecutará settings::getApplicationSecret
Cuando anota un campo con @MockBean, Spring creará una simulación de la clase anotada y la usará para conectar automáticamente todos los beans del contexto de la aplicación.
No debe crear el simulacro usted mismo con
Settings settings = Mockito.mock(Settings.class);esto crearía un segundo simulacro, lo que conduciría al problema descrito.
Solución :
@MockBean private Settings settings; @Before public void before() { Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret"); } @Test public void contextLoads() { String applicationsecret = settings.getApplicationSecret(); System.out.println("Application secret: " + applicationsecret); }