「SpringでDIされるオブジェクトのMockテスト」
「SpringでDIされるオブジェクトのMockテスト その2」の続きです。
前回は単純なAutowiredでしたが、今回はControllerのテストを対象にします。こんな感じのものが対象です。
@RestController
class ControllerA{
@Autowired
ServiceB serviceB;
@Autowired
ServiceC serviceC;
@RequestMapping(value = "/order", method = RequestMethod.GET)
public Map order1(@Valid String item){
return serviceB.save(item);
}
}
(注意)実際に試したコードは業務で使っているコードなので公開できません。考え方の参考にするためにこんな感じで考えましたという程度の情報ですので、その点はご了承ください。
テストコードはgroovyのSpockFramework使ってます。
やったこと
Mockテスト用のcontext.xmlを用意し、ServiceBに対してMockをInjectionするようにしました。
context-apimock.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
">
<!-- 本物をComponent-Scanの対象から外す -->
<context:component-scan base-package="nyasba.example.service">
<context:exclude-filter type="aspectj" expression="nyasba.example.ServiceB*" />
</context:component-scan>
<!-- Mockを生成する -->
<bean id="serviceBMock" name="serviceBMock" class="org.mockito.Mockito"
factory-method="mock">
<constructor-arg value="nyasba.example.service.ServiceB"/>
</bean>
<!-- その他は省略 -->
</beans>
テストコード
@WebAppConfiguration
@ContextConfiguration(locations = "classpath:context-apimock.xml")
class ControllerAMockSpec extends Specification {
@Autowired
ServiceB serviceBMock;
@Autowired
WebApplicationContext wac;
MockMvc mockMvc;
def test() {
setup:
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
// serviceBのsaveが呼ばれるとException発生するようにする
Mockito.when(serviceBMock.save(Mockito.any(String.class)))
.thenThrow(new RuntimeException("あうとーーー!"))
when:
ResultActions resultActions = mockMvc.perform(post("/order").param("item", "test"))
then:
// 省略
cleanup:
Mockito.reset(serviceBMock)
}
}
他に試したこと
いずれも失敗。
- AutowiredしたControllerに対してServiceBのMockを強制的に置き換えた
- AutowiredしたContorollerから
MockMvcBuilders.standaloneSetup(controller)
を使い、mockMvcを作成し、Mockを強制的に置き換えた
業務で使っているコードはAOPを使っているので、Proxyでwrapされたオブジェクトになるので、そのあたりも何か影響しているのかもしれません。。AOP使ってないなら@InjectMockとかでもいけるかもです。
感想
SpringのDIの仕組みはまだまだ全然理解出来たといえるレベルにないですが、こういうのきっかけで作りを調べるのは勉強になるのでよかったです。