web-dev-qa-db-fra.com

Enregistrer le contrôleur annoté @ControllerAdvice dans JUnitTest avec MockMVC

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?

21
Rudolf Schmidt

Depuis Spring 4.2, vous pouvez enregistrer votre ControllerAdvice directement dans votre StandaloneMockMvcBuilder:

MockMvcBuilders
     .standaloneSetup(myController)
     .setControllerAdvice(new MyontrollerAdvice())
     .build();
30
Morten Berg

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();
    }
23
geoand

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();
20
JJZCorum