Ma @ControllerAdvice
Le contrôleur annoté ressemble à ceci:
@ControllerAdvice
public class GlobalControllerExceptionHandler {
@ResponseStatus(value = HttpStatus.UNAUTHORIZED)
@ExceptionHandler(AuthenticationException.class)
public void authenticationExceptionHandler() {
}
}
Bien sûr, mon développement est piloté par les tests et je voudrais utiliser mon gestionnaire d'exceptions dans les tests JUnit. Mon cas de test ressemble à ceci:
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());
}
}
J'ai probablement besoin d'enregistrer la classe ControllerAdvice
. Comment faire ça?
Depuis Spring 4.2, vous pouvez enregistrer votre ControllerAdvice directement dans votre StandaloneMockMvcBuilder:
MockMvcBuilders
.standaloneSetup(myController)
.setControllerAdvice(new MyontrollerAdvice())
.build();
Pour que la configuration complète de Spring MVC soit activée, vous devez utiliser MockMvcBuilders.webAppContextSetup
au lieu de MockMvcBuilders.standaloneSetup
.
Consultez ceci une partie de la documentation Spring pour plus de détails.
Votre code ressemblerait à:
@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());
}
}
Puis à l'intérieur test-config.xml
vous ajouteriez un bean Spring pour AuthenticationService
qui est une maquette.
<bean id="authenticationService" class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="your.package.structure.AuthenticationService"/>
</bean>
Vous pouvez bien sûr utiliser des profils pour injecter la maquette AuthenticationService
dans les tests si vous souhaitez réutiliser votre fichier de configuration Spring habituel au lieu de créer test-config.xml
.
[~ # ~] mise à jour [~ # ~]
Après avoir creusé un peu, j'ai trouvé que StandaloneMockMvcBuilder
retourné par (MockMvcBuilders.standaloneSetup
) est totalement personnalisable. Cela signifie que vous pouvez brancher le résolveur d'exceptions que vous préférez.
Cependant, puisque vous utilisez @ControllerAdvice
, le code ci-dessous ne fonctionnera pas. Si toutefois votre @ExceptionHandler
la méthode était dans le même contrôleur, le code que vous auriez à modifier est le suivant:
mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(new ExceptionHandlerExceptionResolver()).build();
MISE À JOUR 2
Un peu plus de fouilles a permis de savoir comment enregistrer un gestionnaire d'exceptions correct lorsque vous utilisez également @ControllerAdvice
.
Vous devez mettre à jour le code de configuration du test comme suit:
@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();
}
Vous avez dépassé l'exception NestedServletException avec la solution suivante ...
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();