27

My @ControllerAdvice annotated Controller looks like this:

@ControllerAdvice
public class GlobalControllerExceptionHandler {

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

Of course my development is test driven and I would like to use my exception Handler in the JUnit Tests. My Test case looks like this:

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

Probably I need to register the ControllerAdvice Class. How to do that?

4

6 回答 6

37

从 Spring 4.2 开始,您可以将 ControllerAdvice 直接注册到 StandaloneMockMvcBuilder 中:

MockMvcBuilders
     .standaloneSetup(myController)
     .setControllerAdvice(new MyontrollerAdvice())
     .build();
于 2016-11-16T11:14:01.367 回答
25

为了激活完整的 Spring MVC 配置,您需要使用MockMvcBuilders.webAppContextSetup而不是MockMvcBuilders.standaloneSetup.

查看Spring 文档的这一部分以获取更多详细信息。

您的代码如下所示:

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

然后在里面test-config.xml你会添加一个 Spring bean,因为AuthenticationService它是一个 mock。

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

如果要重用AuthenticationService常规 Spring 配置文件而不是创建test-config.xml.


更新

经过一番挖掘,我发现StandaloneMockMvcBuilder( MockMvcBuilders.standaloneSetup) 返回的内容是完全可定制的。这意味着您可以插入您喜欢的任何异常解析器。

但是,由于您使用的是@ControllerAdvice,因此下面的代码将不起作用。但是,如果您的@ExceptionHandler方法在同一个控制器中,则您必须更改的代码如下:

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

更新 2

更多的挖掘给出了如何在使用@ControllerAdvice.

您需要将测试中的设置代码更新为以下内容:

    @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();
    }
于 2014-09-01T11:06:37.947 回答
20

通过以下解决方案克服了 NestedServletException ......

    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();
于 2014-09-11T12:28:46.137 回答
0

如果您有多个建议类,@ExceptionHandler其中每个类都处理一个非常通用的基本异常,例如@ExceptionHandler({Exception.class}),那么您需要按照这个 SO 答案为您的建议类添加一些优先级排序

https://stackoverflow.com/a/19500823/378151

于 2020-08-14T22:32:37.450 回答
0

如果您使用的 junits 版本早于 5 并且由于任何原因无法升级,请考虑如下定义:

@Before
    public void setUp() throws Exception
    {
        MockitoAnnotations.initMocks(this);       
        final StaticApplicationContext applicationContext = new StaticApplicationContext();
        applicationContext.registerSingleton("exceptionHandler", MyExceptionHandler.class);

        final WebMvcConfigurationSupport webMvcConfigurationSupport = new WebMvcConfigurationSupport();
        webMvcConfigurationSupport.setApplicationContext(applicationContext);

        mockMvc = MockMvcBuilders.standaloneSetup(myController).
            setHandlerExceptionResolvers(webMvcConfigurationSupport.handlerExceptionResolver()).
            build();
于 2021-08-27T07:39:17.573 回答
-1

您可以将其添加到您的测试类

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

然后添加exceptionResolver到你的 MockMvc

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    mockMvc = MockMvcBuilders.standaloneSetup(controller)
               .setHandlerExceptionResolvers(this.exceptionResolver).build();
}
于 2015-10-16T16:03:27.573 回答