web-dev-qa-db-fra.com

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

J'ai un ensemble de contrôleurs dans l'application et une classe annotée comme @ControllerAdvice qui configure certains éléments de données qui sont utilisés dans chacun de ces contrôleurs. J'utilise Spring MVC 3.2 et avoir des Junits pour ces contrôleurs. Lorsque j'exécute Junit, le contrôle ne va pas dans la classe ControllerAdvice où il fonctionne correctement si je déploie l'application dans Tomcat et soumets une demande via le navigateur.

Des pensées s'il vous plaît?.

46
Arun

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

En conséquence, Spring test a introduit la possibilité d'enregistrer les classes @ControllerAdvice Dans le générateur en 4.2. Si vous utilisez Spring Boot alors vous aurez besoin de la version 1.3.0 ou ultérieure.

Avec cette amélioration, si vous utilisez une configuration autonome, vous pouvez définir une ou plusieurs instances ControllerAdvice comme ceci:

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

Remarque: le nom setControllerAdvice() peut ne pas le rendre immédiatement évident mais vous pouvez lui passer de nombreuses instances, car il a une variable signature args.

84
Matt Byrne

Supposons que vous ayez 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'exceptions.

@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;
}
41
Eugene To

J'ai rencontré un problème similaire lors de la tentative de test de ExceptionHandler annoté avec @ControllerAdvice. Dans mon cas, j'ai dû ajouter @Configuration fichier avec @EnableWebMvc annotation à @ContextConfiguration sur la classe de test.

Donc, mon test ressemblait à ceci:

@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 a réussi.

18
tunguski

Cela 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)).andReturn();

    }

}
7
Bikesh M

Je lutte avec la même chose depuis un certain temps. Après de nombreuses recherches, la meilleure référence était la documentation Spring:

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

En bref, si vous testez simplement un contrôleur et ses méthodes, vous pouvez utiliser la méthode 'standaloneSetup' qui crée une configuration Spring MVC simple. Cela n'inclura pas votre gestionnaire d'erreurs 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 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;

    // ...

}
6
Neil D

L'exemple de code @tunguski fonctionne mais il est utile de comprendre comment les choses fonctionnent. Ce n'est qu'une façon de configurer les choses.

@EnableWebMvc équivaut à suivre dans un fichier de configuration Spring

<mvc:annotation-driven />

Pour que les choses fonctionnent, vous devez essentiellement initialiser Spring Mvc et charger tous vos contrôleurs et références de bean. Donc, ce qui suit pourrait être une configuration valide ainsi qu'une alternative

Voici comment configurer la classe de 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();
        }
    }

Et ce qui suit pourrait être la configuration du ressort pour le test

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

J'ai rencontré ce problème lors de l'écriture des tests du contrôleur avec spock (groovy). Ma classe de test a été écrite à l'origine comme:

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

Cela a causé ControllerAdvice à être ignoré. Changer le code en Autowire les mocks a résolu le problème.

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

  @AutowiredMock
  FooService FooService

  @Autowired
  MockMvc mockMvc
0
Mustafa

Je soupçonne que vous devez utiliser asyncDispatch dans votre test; le cadre de test régulier est rompu avec les contrôleurs asynchrones.

Essayez l'approche dans: https://github.com/spring-projects/spring-framework/blob/master/spring-test/src/test/Java/org/springframework/test/web/servlet/samples /standalone/AsyncTests.Java

0
Eddy

Vous devrez fournir plus d'informations, et peut-être quelques fichiers de code et/ou de configuration réels, avant de pouvoir attendre des réponses spécifiques. Cela dit, sur la base du peu que vous avez fourni, il semble que le bean annoté ne soit pas chargé.

Essayez d'ajouter ce qui suit à votre test applicationContext.xml (ou à un fichier de configuration Spring équivalent, si vous en utilisez un).

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

Alternativement, vous devrez peut-être charger "manuellement" les contextes dans les tests en incluant les annotations suivantes avant votre classe de test:

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

Bonne chance!

0
Liam