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?.
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.
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;
}
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.
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();
}
}
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:
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;
// ...
}
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" />
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
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
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!