87 votes

Le test unitaire des contrôleurs de Spring MVC n'appelle pas @ControllerAdvice

J'ai un ensemble de contrôleurs dans l'application et une classe annotée en tant que @ControllerAdvice qui définit certains éléments de données qui sont utilisés dans chacun de ces contrôleurs. J'utilise Spring MVC 3.2 et ont des Junits pour ces contrôleurs. Lorsque j'exécute le Junit, le contrôle n'est pas dirigé vers le contrôleur ControllerAdvice alors qu'elle fonctionne bien si je déploie l'application dans la classe Tomcat et soumettre une demande par le biais du navigateur.

Avez-vous des idées ?

132voto

Matt Byrne Points 462

Après avoir utilisé la réponse de @eugene-to et une autre similaire aquí J'ai trouvé des limitations et soulevé un problème sur Spring : https://jira.spring.io/browse/SPR-12751

En conséquence, le test de Spring a introduit la possibilité d'enregistrer @ControllerAdvice dans le constructeur en 4.2. Si vous utilisez Spring Boot alors vous aurez besoin de la version 1.3.0 ou d'une version ultérieure.

Grâce à cette amélioration, si vous utilisez une installation autonome, vous pouvez définir un ou plusieurs éléments de l'installation. ControllerAdvice comme ça :

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

Note : le nom setControllerAdvice() Ce n'est pas forcément évident, mais vous pouvez lui passer plusieurs instances, puisqu'elle a une signature var-args.

42voto

Eugene To Points 1213

Supposons que vous avez la classe MyControllerAdvice annotée avec @ControllerAdvice qui a des méthodes annotées avec @ExceptionHandler. Pour MockMvc, vous pouvez facilement ajouter cette classe comme résolveur d'exception.

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

26voto

tunguski Points 745

J'ai eu un problème similaire en essayant de tester ExceptionHandler annoté avec @ControllerAdvice . Dans mon cas, j'ai dû ajouter @Configuration avec @EnableWebMvc à l'annotation @ContextConfiguration sur la classe de test.

Mon test ressemblait donc à ça :

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

Avec @EnableWebMvc configuration, mon test est passé.

19voto

Bikesh M Annur Points 3899

Ce code fonctionne pour moi :

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

7voto

Neil D Points 71

Je me débats avec la même chose depuis un certain temps. Après avoir beaucoup creusé, la meilleure référence était la documentation de Spring :

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

En résumé, si vous testez simplement un contrôleur et ses méthodes, vous pouvez utiliser l'option standaloneSetup qui crée une configuration Spring MVC simple. Cette méthode pas inclure votre gestionnaire d'erreur que vous annotez avec @ControllerAdvice.

private MockMvc mockMvc;

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

// ...

Pour créer une configuration Spring MVC plus complète qui fait contient votre gestionnaire d'erreurs, vous devez utiliser la configuration suivante :

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

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Autowired
    private AccountService accountService;

    // ...

}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X