2013-03-08 10 views
29

Ho un set di controller nell'applicazione e una classe annotata come @ControllerAdvice che imposta determinati elementi di dati che vengono utilizzati in ciascuno di questi controller. Sto usando Spring MVC 3.2 e ho Junits per questi controller. Quando eseguo il Junit, il controllo non passa alla classe , ma funziona correttamente se distribuisco l'app in Tomcat e inoltro una richiesta tramite browser.Spring MVC Controllers Unit Test non chiama @ControllerAdvice

Qualche idea per favore ?.

+1

Apprezzare un aggiornamento su questo. Sto affrontando un problema simile in cui il metodo annotato @ExceptionHandler (Exception.class) in una classe @ControllerAdvice non viene chiamato tramite unit test - webAppContextSetup (wac) .build(). Il metodo annotato ExceptionHandler viene chiamato quando distribuito come webapp. – kctang

risposta

0

Prima di poter aspettarsi risposte specifiche, è necessario fornire ulteriori informazioni e magari alcuni file e/o file di configurazione effettivi. Detto questo, in base al poco che hai fornito, sembra che il bean annotato non venga caricato.

Provare ad aggiungere quanto segue al test applicationContext.xml (o un file di configurazione Spring equivalente, se ne si utilizza uno).

<context:component-scan base-package="com.example.path.to.package" /> 

In alternativa, potrebbe essere necessario 'manualmente' caricare i contesti all'interno dei test includendo le seguenti annotazioni prima della classe di test:

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration("/applicationContext.xml") 

Buona fortuna!

4

Ho lottato con la stessa cosa per un po 'di tempo. Dopo molte scavo, il miglior riferimento è stata la documentazione di primavera:

http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/testing.html#spring-mvc-test-framework

In breve, se si sta semplicemente testando un controller e dei suoi metodi è possibile utilizzare il metodo 'standaloneSetup' che crea una semplice molla Configurazione MVC. Questo sarà non includi il gestore degli errori che annoti con @ControllerAdvice.

private MockMvc mockMvc; 

@Before 
public void setup() { 
    this.mockMvc = MockMvcBuilders.standaloneSetup(new AccountController()).build(); 
} 

// ... 

Per creare una configurazione più completa Spring MVC che fa contenere il vostro gestore degli errori è necessario utilizzare la seguente configurazione:

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration("test-servlet-context.xml") 
public class AccountTests { 

    @Autowired 
    private WebApplicationContext wac; 

    private MockMvc mockMvc; 

    @Autowired 
    private AccountService accountService; 

    // ... 

} 
+0

È ancora necessario inizializzare il mockMvc, che non è mostrato nell'esempio precedente: @Before public void setUp() { mockMvc = MockMvcBuilders.webAppContextSetup (webApplicationContext) .build(); } –

+1

È possibile utilizzare .setControllerAdvice (xxx) con StandaloneMockMvcBuilder per iniettare il proprio bean ControllerAdvice – frno

12

ho avuto problema simile quando si cerca di testare ExceptionHandler annotato con @ControllerAdvice. Nel mio caso ho dovuto aggiungere il file @Configuration con annotazione @EnableWebMvc a @ContextConfiguration nella classe di test.

Quindi il mio test si presentava così:

@RunWith(SpringJUnit4ClassRunner.class) 
@WebAppConfiguration 
@ContextConfiguration(classes = { 
    RestProcessingExceptionHandler.class, 
    TestConfiguration.class, 
    RestProcessingExceptionThrowingController.class }) 
public class TestRestProcessingExceptionHandler { 


    private MockMvc mockMvc; 
    @Autowired 
    WebApplicationContext wac; 


    @Before 
    public void setup() { 
    mockMvc = webAppContextSetup(wac).build(); 
    } 


    @Configuration 
    // !!! this is very important - conf with this annotation 
    //  must be included in @ContextConfiguration 
    @EnableWebMvc 
    public static class TestConfiguration { } 


    @Controller 
    @RequestMapping("/tests") 
    public static class RestProcessingExceptionThrowingController { 


    @RequestMapping(value = "/exception", method = GET) 
    public @ResponseBody String find() { 
     throw new RestProcessingException("global_error_test"); 
    } 
    } 


    @Test 
    public void testHandleException() throws Exception { 
    mockMvc.perform(get("/tests/exception")) 
     .andExpect(new ResultMatcher() { 


      @Override 
      public void match(MvcResult result) throws Exception { 
      result.getResponse().getContentAsString().contains("global_error_test"); 
      } 
     }) 
     .andExpect(status().isBadRequest()); 
    } 
} 

Con @EnableWebMvc configurazione Il test è stato superato.

+0

grazie, la tua configurazione funziona perfettamente! Assegnerò la taglia! – balteo

+0

@EnableWebMvc assenza era il problema, grazie. – raonirenosto

+0

java.lang.IllegalStateException: Impossibile caricare ApplicationContext :( – ptimson

36

Supponiamo di avere classe MyControllerAdvice annotata con @ControllerAdvice con metodi annotati con @ExceptionHandler. Per MockMvc puoi facilmente aggiungere questa classe come risolutore di eccezioni.

@Before 
public void beforeTest() { 
    MockMvc mockMvc = standaloneSetup(myControllers) 
     .setHandlerExceptionResolvers(createExceptionResolver()) 
     .build(); 
} 

private ExceptionHandlerExceptionResolver createExceptionResolver() { 
    ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() { 
     protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { 
      Method method = new ExceptionHandlerMethodResolver(MyControllerAdvice.class).resolveMethod(exception); 
      return new ServletInvocableHandlerMethod(new MyControllerAdvice(), method); 
     } 
    }; 
    exceptionResolver.afterPropertiesSet(); 
    return exceptionResolver; 
} 
+0

Grazie, funziona perfettamente! –

+0

Funziona come previsto, grazie! – aksamit

+1

questo non funziona per me utilizzando la gestione delle richieste asincrone – hvgotcodes

1

codice di esempio @tunguski funziona, ma vale la pena di capire come funzionano le cose. Questo è solo un modo per sistemare le cose.

@EnableWebMvc equivale a seguito in un file di configurazione di primavera

<mvc:annotation-driven />

In sostanza cosa c'è da lavorare è necessario inizializzare Spring MVC e caricare tutti i controller ei riferimenti di fagioli. Quindi, a seguito potrebbe essere una configurazione valida, nonché un supplente

seguito è come si farebbe impostare la classe di test

@RunWith(SpringJUnit4ClassRunner.class) 
    @ContextConfiguration(locations = { "classpath: "classpath:test-context.xml" }) 
    @WebAppConfiguration  
    public class BaseTest { 

     @Autowired 
     WebApplicationContext wac; 

     private MockMvc mockMvc; 

     @Before 
     public void setUp() { 
      mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); 
     } 
    } 

E seguendo potrebbe essere la configurazione di primavera per il test

<mvc:annotation-driven /> 
<context:component-scan base-package="com.base.package.controllers" /> 
51

Dopo utilizzando la risposta da @ eugene-a e un'altra simile here ho trovato limitazioni e sollevato un problema in primavera: https://jira.spring.io/browse/SPR-12751

Come risultato, il test Spring ha introdotto la possibilità di registrare le classi @ControllerAdvice nel builder in 4.2. Se stai usando Spring Boot allora avrai bisogno della versione 1.3.0 o successiva.

Con questo miglioramento, se si utilizza il programma di installazione standalone quindi è possibile impostare uno o più ControllerAdvice casi in questo modo:

mockMvc = MockMvcBuilders.standaloneSetup(yourController) 
      .setControllerAdvice(new YourControllerAdvice()) 
      .build(); 

Nota: il nome setControllerAdvice() potrebbe non rendere immediatamente evidente, ma si può passare molte istanze ad esso, dal momento che ha una firma var-args.

+0

Grande. Funziona. Grazie –

+2

Questa dovrebbe essere la risposta accettata – frno

+0

I Aggiungi qualcosa per thoses, come me, che sono infastiditi dal validatore primavera con il trucco di cui sopra: this.mockMvc = MockMvcBuilders.standaloneSetup (yourController) .setValidator (finto (Validator.class)) . SetControllerAdvice (new CaissierExceptionHandler()) .build(); –

-1

Questo sta lavorando per me

public class MyGlobalExceptionHandlerTest { 

    private MockMvc mockMvc; 

    @Mock 
    HealthController healthController; 

    @BeforeTest 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     mockMvc = MockMvcBuilders.standaloneSetup(healthController).setControllerAdvice(new GlobalExceptionHandler()) 
      .build(); 
    } 

    @Test(groups = { "services" }) 
    public void testGlobalExceptionHandlerError() throws Exception { 

     Mockito.when(healthController.health()).thenThrow(new RuntimeException("Unexpected Exception")); 

     mockMvc.perform(get("/health")).andExpect(status().is(500)).andReturn(); 

    } 

} 
+0

con quale versione di spring boot ha funzionato? con 1.5.6 questo codice non chiama mai il mio controlleradvice –

+0

questa sarebbe l'ovvia soluzione minima, ma non funziona .. – Dudelilama

0

ho incontrato questo problema durante la scrittura di test controller con spock (Groovy). La mia classe di test è stata originariamente scritta come:

@AutoConfigureMockMvc(secure = false) 
@SpringBootTest 
@Category(RestTest) 
class FooControllerTest extends Specification { 
    def fooService = Mock(FooService) 
    def underTest = new FooController(FooService) 
    def mockMvc = MockMvcBuilders.standaloneSetup(underTest).build() 
.... 
} 

Ciò ha causato che ControllerAdvice venisse ignorato. Cambiando il codice per Autowire i mock hanno risolto il problema.

@AutoConfigureMockMvc(secure = false) 
@SpringBootTest 
@Category(RestTest) 
class FooControllerTest extends Specification { 

    @AutowiredMock 
    FooService FooService 

    @Autowired 
    MockMvc mockMvc