2014-09-01 13 views
19

Meine @ControllerAdvice kommentierten-Controller wie folgt aussieht:Register @ControllerAdvice kommentierten Controller JUnitTest mit MockMVC

@ControllerAdvice 
public class GlobalControllerExceptionHandler { 

    @ResponseStatus(value = HttpStatus.UNAUTHORIZED) 
    @ExceptionHandler(AuthenticationException.class) 
    public void authenticationExceptionHandler() { 
    } 
} 

Natürlich meine Entwicklungstest gefahren wird, und ich möchte meine Exception-Handler in den JUnit-Tests verwenden. Mein Testfall sieht wie folgt aus:

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

Wahrscheinlich muss ich die ControllerAdvice Klasse registrieren. Wie geht das?

+0

Haben Sie die Option 'MockMvcBuilders.webAppContextSetup' versucht, wie unter http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test- beschrieben. Framework anstelle von 'MockMvcBuilders.standaloneSetup'? – geoand

Antwort

20

Damit die vollständige Spring MVC-Konfiguration aktiviert wird, müssen Sie MockMvcBuilders.webAppContextSetup anstelle von MockMvcBuilders.standaloneSetup verwenden.

Weitere Informationen finden Sie in der Dokumentation zum Frühling unter der Nummer this.

Ihr Code würde wie folgt aussehen:

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

Dann innen test-config.xml Sie einen Spring-Bean für AuthenticationService hinzufügen würden, dass ein Modell ist.

<bean id="authenticationService" class="org.mockito.Mockito" factory-method="mock"> 
    <constructor-arg value="your.package.structure.AuthenticationService"/> 
</bean> 

Sie könnten natürlich Nutzungsprofile, die Mock AuthenticationService in den Tests injizieren, wenn Sie möchten, Ihre regelmäßige Spring-Konfigurationsdatei wieder zu verwenden anstatt test-config.xml zu schaffen.


UPDATE

Nachdem sich ein wenig zu graben, fand ich, dass StandaloneMockMvcBuilder von (MockMvcBuilders.standaloneSetup) zurück total kundengerecht. Das bedeutet, dass Sie den von Ihnen bevorzugten Ausnahme-Resolver verwenden können.

Da Sie jedoch @ControllerAdvice verwenden, wird der folgende Code nicht funktionieren. Wenn jedoch Ihre @ExceptionHandler Methode in demselben Controller den gesamten Code war würden Sie ändern müssen, ist die folgende:

mockMvc = MockMvcBuilders.standaloneSetup(controller).setHandlerExceptionResolvers(new ExceptionHandlerExceptionResolver()).build(); 

UPDATE 2

mehr Einige, um die Antwort gab graben, wie Sie kann einen korrekten Ausnahmebehandler registrieren, wenn Sie auch @ControllerAdvice verwenden.

benötigen Sie den Setup-Code in dem Test auf den folgende Aktualisierung:

@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(); 
    } 
+0

Danke für Ihren Beitrag, es hat viel geholfen. Aber gibt es eine andere, einfachere Lösung als webApplicationContext? Ich habe versucht, die Klasse AuthenticationException direkt zu kommentieren, und es funktioniert mit Standaloneserver. Das Problem ist, dass ich den Rest von der Serviceebene trennen und nicht beide Ebenen mit Anmerkungen von der anderen Ebene mischen möchte. Die Ausnahmebehandlung sollte im Rest-Layer verwaltet werden. Gibt es eine andere Möglichkeit? –

+0

@RudolfSchmidt Ich bin froh, dass es dir geholfen hat! Sie haben Recht, Interessen zu trennen! Aber ich verstehe nicht, was das Problem mit 'webApplicationContext' ist. Kannst du ein wenig darauf eingehen? Mir ist keine andere Lösung bekannt, die die Ausnahmebehandlung in einer Testumgebung korrekt registriert. – geoand

+0

@RudolfSchmidt Ich habe weitere Informationen gefunden, die hilfreich sein könnten. Wird in Kürze aktualisiert – geoand

18

Got vorbei an der NestedServletException mit folgenden Lösung ...

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(); 
+0

Danke für Ihre Antwort! Basierend darauf habe ich eine Lösung für den RestAssuredMockMvc-Ansatz gefunden: http://StackOverflow.com/a/38435008/2239713 – ilyailya

-1

Sie können diese Klasse zu Ihrem Test hinzufügen

@Autowired 
@Qualifier("handlerExceptionResolver") 
void setExceptionResolver(HandlerExceptionResolver resolver) 
{ 
    this.exceptionResolver = resolver; 
} 

und fügen Sie dann den exceptionResolver zu Ihrem MockMvc

@Before 
public void setup() { 
    MockitoAnnotations.initMocks(this); 
    mockMvc = MockMvcBuilders.standaloneSetup(controller) 
       .setHandlerExceptionResolvers(this.exceptionResolver).build(); 
} 
+1

Mir scheint es nicht sinnvoll, @Autowired im Standalone-Modus zu verwenden. Sie sollten nicht einmal einen Federkontext im Standalone-Modus haben. –

20

Seit dem Frühjahr 4.2 können Sie Ihr ControllerAdvice direkt in Ihr StandaloneMockMvcBuilder registrieren :

MockMvcBuilders 
    .standaloneSetup(myController) 
    .setControllerAdvice(new MyontrollerAdvice()) 
    .build(); 
+0

Schön, viel einfacher als die bisherigen Lösungen. –

Verwandte Themen