ぼくの @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
クラスを登録する必要があります。どうやってするか?
Spring 4.2以降、ControllerAdviceを直接StandaloneMockMvcBuilderに登録できます。
MockMvcBuilders
.standaloneSetup(myController)
.setControllerAdvice(new MyontrollerAdvice())
.build();
完全な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();
}
次の解決策で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();