java 使用 MockMVC 在 JUnitTest 中注册 @ControllerAdvice 注释的控制器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25604215/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 08:19:48  来源:igfitidea点击:

Register @ControllerAdvice annotated Controller in JUnitTest with MockMVC

javaspringspring-mvcjunitmockmvc

提问by Rudolf Schmidt

My @ControllerAdviceannotated Controller looks like this:

我的带@ControllerAdvice注释的控制器如下所示:

@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:

当然,我的开发是测试驱动的,我想在 JUnit 测试中使用我的异常处理程序。我的测试用例如下所示:

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 ControllerAdviceClass. How to do that?

可能我需要注册ControllerAdvice班级。怎么做?

回答by Morten Berg

Since Spring 4.2, you can register your ControllerAdvice directly into your StandaloneMockMvcBuilder:

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

MockMvcBuilders
     .standaloneSetup(myController)
     .setControllerAdvice(new MyontrollerAdvice())
     .build();

回答by geoand

In order for the full Spring MVC configuration to get activated, you need to use MockMvcBuilders.webAppContextSetupinstead of MockMvcBuilders.standaloneSetup.

为了使全Spring MVC的配置得到激活,你需要使用MockMvcBuilders.webAppContextSetup的替代MockMvcBuilders.standaloneSetup

Check out thispart of the Spring documentation for more details.

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

Your code would look like:

您的代码如下所示:

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

Then inside test-config.xmlyou would add a Spring bean for AuthenticationServicethat is a mock.

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

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


You could of course use profiles to inject the mock AuthenticationServicein the tests if want to reuse your regular Spring configuration file instead of creating test-config.xml.

当然使用的配置文件,你可以注入模拟AuthenticationService的测试,如果希望再次使用常规的Spring配置文件,而不是创造test-config.xml



UPDATE

更新

After digging around a bit, I found that StandaloneMockMvcBuilderreturned by (MockMvcBuilders.standaloneSetup) is totally customizable. That means that you can plug in whatever exception resolver you prefer.

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

However since you are using @ControllerAdvice, the code below will not work. If however your @ExceptionHandlermethod was inside the same controller the code all you would have to change is the following:

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

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


UPDATE 2

更新 2

Some more digging gave the answer to how you can register a correct exception handler when you are also using @ControllerAdvice.

更多的挖掘给出了当您还使用@ControllerAdvice.

You need to update the setup code in the test to the following:

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

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

回答by JJZCorum

Got past the NestedServletException with the following solution...

使用以下解决方案克服了 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();

回答by Luis Carlos Fonseca Acu?a

You can add this to your test class

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

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

and then add the exceptionResolverto your MockMvc

然后将其添加exceptionResolver到您的 MockMvc

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    mockMvc = MockMvcBuilders.standaloneSetup(controller)
               .setHandlerExceptionResolvers(this.exceptionResolver).build();
}