2014-09-01 14 views
19

Moja @ControllerAdvice adnotacją Kontroler wygląda następująco:Rejestracja @ControllerAdvice adnotacją Controller w JUnitTest z MockMVC

@ControllerAdvice 
public class GlobalControllerExceptionHandler { 

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

Oczywiście mój rozwój Test Driven i chciałbym używać wyjątku Handler w testy JUnit. Mój przypadek testowy wygląda następująco:

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

Prawdopodobnie muszę zarejestrować ControllerAdvice klasie. Jak to zrobić?

+0

Czy próbowałeś opcji 'MockMvcBuilders.webAppContextSetup' jak opisano na http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test- framework zamiast 'MockMvcBuilders.standaloneSetup'? – geoand

Odpowiedz

20

Aby aktywować pełną konfigurację Spring MVC, należy użyć MockMvcBuilders.webAppContextSetup zamiast MockMvcBuilders.standaloneSetup.

Aby uzyskać więcej informacji, zapoznaj się z częścią Dokumentacja sprężynowa pod numerem this.

Twój kod będzie wyglądać następująco:

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

Następnie wewnątrz test-config.xml byś dodać Wiosna fasoli dla AuthenticationService że jest fałszywa.

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

Można profili ruchu oczywiście wstrzyknąć mock AuthenticationService w testach jeśli chcesz ponownie użyć zwykły plik konfiguracyjny Wiosna zamiast tworzyć test-config.xml.


UPDATE

Po wykopaniu się trochę, to okazało się, że StandaloneMockMvcBuilder zwrócony przez (MockMvcBuilders.standaloneSetup) jest w pełni konfigurowalny. Oznacza to, że możesz podłączyć dowolny preferowany wyjątek.

Ponieważ jednak używasz @ControllerAdvice, poniższy kod nie będzie działać. Jeśli wszystko trzeba by zmienić jednak metoda @ExceptionHandler był wewnątrz samego kontrolera kod jest następujący:

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

UPDATE 2

Niektóre bardziej kopanie dał odpowiedź na to, jak można zarejestrować prawidłową procedurę obsługi wyjątku, gdy używasz także @ControllerAdvice.

Musisz zaktualizować kod instalacyjny w teście na następujące kwestie:

@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

Dzięki za post, bardzo pomogło. Ale czy istnieje jeszcze prostsze rozwiązanie niż webApplicationContext? Próbowałem bezpośrednio opisywać klasę AuthenticationException bezpośrednio i działa ona z serwerem standalone. Problem polega na tym, że chcę oddzielić resztę od warstwy usługi i nie mieszać obu warstw z adnotacjami z innej warstwy. Obsługa wyjątków powinna być zarządzana w warstwie reszty. Czy jest jeszcze inna możliwość? –

+0

@RudolfSchmidt Cieszę się, że ci pomogło! Masz rację, chcąc rozdzielić wątpliwości! Ale nie rozumiem, jaki jest problem z 'webApplicationContext'. Czy możesz trochę o tym powiedzieć? Nie jestem świadomy żadnego innego rozwiązania, które zarejestruje poprawnie obsługę wyjątków w środowisku testowym. – geoand

+0

@RudolfSchmidt Znalazłem więcej informacji, które mogą pomóc. Zaktualizuje się wkrótce – geoand

18

Got piłkę NestedServletException z następującym rozwiązaniem ...

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

Dzięki za odpowiedź! Na podstawie tego znalazłem się rozwiązanie dla podejścia RestAssuredMockMvc: http://stackoverflow.com/a/38435008/2239713 – ilyailya

-1

Można dodać do swojej klasy testowej

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

a następnie dodaj exceptionResolver do MockMvc

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

Dla mnie nie wydaje się sens używać @Autowired w trybie autonomicznym. Nie powinieneś nawet mieć kontekstu sprężynowego w trybie autonomicznym. –

20

Od wiosny 4.2, można zarejestrować swój ControllerAdvice bezpośrednio do StandaloneMockMvcBuilder :

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

Ładne, znacznie prostsze niż poprzednie rozwiązania. –