java - tutorial - Registre el controlador anotado @ControllerAdvice en JUnitTest con MockMVC
test spring controller (4)
Mi @ControllerAdvice
anotado @ControllerAdvice
ve así:
@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AuthenticationException.class)
public void authenticationExceptionHandler() {
}
}
Por supuesto, mi desarrollo está basado en pruebas y me gustaría usar mi Administrador de excepciones en las Pruebas JUnit. Mi caso de prueba se ve así:
public class ClientQueriesControllerTest {
private MockMvc mockMvc;
@InjectMocks
private ClientQueriesController controller;
@Mock
private AuthenticationService authenticationService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void findAllAccountRelatedClientsUnauthorized() throws Exception {
when(authenticationService.validateAuthorization(anyString())).thenThrow(AuthenticationException.class);
mockMvc.perform(get("/rest/clients").header("Authorization", UUID.randomUUID().toString()))
.andExpect(status().isUnauthorized());
}
}
Probablemente necesito registrar la clase ControllerAdvice
. ¿Como hacer eso?
Desde la primavera 4.2, puede registrar su ControllerAdvice directamente en su StandaloneMockMvcBuilder:
MockMvcBuilders
.standaloneSetup(myController)
.setControllerAdvice(new MyontrollerAdvice())
.build();
Para que la configuración completa de Spring MVC se active, debe usar MockMvcBuilders.webAppContextSetup
lugar de MockMvcBuilders.standaloneSetup
.
Echa un vistazo a docs.spring.io/spring-framework/docs/current/… parte de la documentación de Spring para más detalles.
Su código se vería como:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("test-config.xml")
public class ClientQueriesControllerTest {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private AuthenticationService authenticationService;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void findAllAccountRelatedClientsUnauthorized() throws Exception {
when(authenticationService.validateAuthorization(anyString())).thenThrow(AuthenticationException.class);
mockMvc.perform(get("/rest/clients").header("Authorization", UUID.randomUUID().toString()))
.andExpect(status().isUnauthorized());
}
}
Luego, dentro de test-config.xml
usted agregaría un bean Spring para AuthenticationService
que es un simulacro.
<bean id="authenticationService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="your.package.structure.AuthenticationService"/>
</bean>
Por supuesto, puede usar perfiles para inyectar el AuthenticationService
simulado en las pruebas si desea reutilizar su archivo de configuración Spring normal en lugar de crear test-config.xml
.
ACTUALIZAR
Después de investigar un poco, descubrí que StandaloneMockMvcBuilder
devuelto por ( MockMvcBuilders.standaloneSetup
) es totalmente personalizable. Eso significa que puede conectar cualquier resolución que prefiera.
Sin embargo, dado que está utilizando @ControllerAdvice
, el código siguiente no funcionará. Sin embargo, si su método @ExceptionHandler
estaba dentro del mismo controlador, el código que tendría que cambiar es el siguiente:
mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(new ExceptionHandlerExceptionResolver()).build();
ACTUALIZACIÓN 2
Algunas excavaciones adicionales dieron la respuesta a cómo puede registrar un controlador de excepciones correcto cuando también está utilizando @ControllerAdvice
.
Debe actualizar el código de configuración en la prueba a lo siguiente:
@Before
public void setUp() throws Exception {
final ExceptionHandlerExceptionResolver exceptionHandlerExceptionResolver = new ExceptionHandlerExceptionResolver();
//here we need to setup a dummy application context that only registers the GlobalControllerExceptionHandler
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerBeanDefinition("advice", new RootBeanDefinition(GlobalControllerExceptionHandler.class, null, null));
//set the application context of the resolver to the dummy application context we just created
exceptionHandlerExceptionResolver.setApplicationContext(applicationContext);
//needed in order to force the exception resolver to update it''s internal caches
exceptionHandlerExceptionResolver.afterPropertiesSet();
mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(exceptionHandlerExceptionResolver).build();
}
Pasé la excepción NestedServletException con la siguiente solución ...
final StaticApplicationContext applicationContext = new StaticApplicationContext();
applicationContext.registerSingleton("exceptionHandler", GlobalControllerExceptionHandler.class);
final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
webMvcConfigurationSupport.setApplicationContext(applicationContext);
mockMvc = MockMvcBuilders.standaloneSetup(controller).
setHandlerExceptionResolvers(webMvcConfigurationSupport.handlerExceptionResolver()).
build();
Puedes agregar esto a tu clase de prueba
@Autowired
@Qualifier("handlerExceptionResolver")
void setExceptionResolver(HandlerExceptionResolver resolver)
{
this.exceptionResolver = resolver;
}
y luego agrega el exceptionResolver
a tu MockMvc
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(controller)
.setHandlerExceptionResolvers(this.exceptionResolver).build();
}