web-dev-qa-db-ja.com

@ControllerAdvice注釈付きコントローラーをJUnitTestのMockMVCに登録する

ぼくの @ControllerAdvice注釈付きコントローラーは次のようになります。

@ControllerAdvice
public class GlobalControllerExceptionHandler {

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

もちろん、私の開発はテスト駆動であり、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());
    }
}

おそらくControllerAdviceクラスを登録する必要があります。どうやってするか?

21
Rudolf Schmidt

Spring 4.2以降、ControllerAdviceを直接StandaloneMockMvcBuilderに登録できます。

MockMvcBuilders
     .standaloneSetup(myController)
     .setControllerAdvice(new MyontrollerAdvice())
     .build();
30
Morten Berg

完全なSpring MVC構成をアクティブ化するには、MockMvcBuilders.webAppContextSetupではなくMockMvcBuilders.standaloneSetupを使用する必要があります。

詳細については、Springドキュメントの this の部分を確認してください。

コードは次のようになります。

@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内に、モックであるAuthenticationServiceのSpring Beanを追加します。

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

もちろん、test-config.xmlを作成する代わりに通常のSpring構成ファイルを再利用したい場合は、プロファイルを使用してモックAuthenticationServiceをテストに挿入できます。


[〜#〜]更新[〜#〜]

少し調べたところ、(MockMvcBuilders.standaloneSetup)から返されたStandaloneMockMvcBuilderは完全にカスタマイズ可能であることがわかりました。つまり、好きな例外リゾルバをプラグインできるということです。

ただし、@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();
    }
23
geoand

次の解決策で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();
20
JJZCorum