調べてもいろんな方法があったので備忘録としてメモしておく。
かなり適当ですがこんな感じにSpringFrameworkでDIしているRepositoryBのみをMockにし、RepositoryBへのInputをテストしたいというのが動機。
class ServiceA{
@Autowired
RepositoryB repositoryB;
@Autowired
RepositoryC repositoryC;
public void order1(String item){
repositoryB.save(item);
}
public void order2(String item){
repositoryB.save(item);
repositoryC.save(item);
}
}
(注意)実際に試したコードは業務で使っているコードなので公開できません。考え方の参考にするためにこんな感じで考えましたという程度の情報ですので、その点はご了承ください。
テストコードはgroovyのSpockFramework使ってます。
(1) Mockitoを使う
依存関係にmockitoを追加。
testCompile "org.mockito:mockito-all:1.10.19"
こんな感じで書くとRepositoryBだけなら上手くいくのですが、RepositoryCを使うとエラーになります。どうもRepositoryCもMockにしないといけないっぽい。
@ContextConfiguration(locations = "classpath:context.xml")
class ServiceASpec extends Specification {
@InjectMocks
private ServiceA serviceA
@Mock
private RepositoryB repositoryB
def setup() {
MockitoAnnotations.initMocks(this)
}
def "RepositoryBをMockにしたテスト"() {
when:
sut.order1("test")
then:
Mockito.verify(repositoryB).save("test")
}
}
(2) Springockitoを使う
依存関係にSpringockitoを追加。
testCompile "org.kubek2k:springockito-annotations:1.0.9"
これだとRepositoryCを使ってもMockされてない状態で動くはずでした。
ただ、実際業務で使っているコードで実装してみるとIllegalStateExceptionが出ました。RepositoryCは呼ばれているようでしたがなぜかはよくわからず断念。
@ContextConfiguration(locations = "classpath:context.xml", loader = SpringockitoContextLoader.class)
class ServiceASpec extends Specification {
@Autowired
private ServiceA serviceA
@ReplaceWithMock
@Autowired
private RepositoryB repositoryB
def "RepositoryBをMockにしたテスト"() {
when:
sut.order1("test")
then:
Mockito.verify(repositoryB).save("test")
}
}
(3) SpockのMockを使う
SpockにもMockあるのでそれで考えてみた。
RepositoryBのMockを作って強制的に置き換える方法ですが、意図したことはできました。
@ContextConfiguration(locations = "classpath:context.xml")
class ServiceASpec extends Specification {
@Autowired
private ServiceA serviceA
def setup() {
RepositoryB repositoryB = Mock()
ServiceA.metaClass.setAttribute(serviceA, "repositoryB", repositoryB)
}
def "RepositoryBをMockにしたテスト"() {
when:
sut.order1("test")
then:
// repositoryB.save("test")が1回呼ばれた
1 * repositoryB.save("test")
// 上記以外でMockが呼ばれていない
0 * _
}
}
まとめ
なんとかやりたかったことはできましたが、表面的にしか理解できていないのですっきりせず。。もっと勉強しなきゃと思いました。
2015/4/24 追記
「SpringでDIされるオブジェクトのMockテストその2」で
(4)Mockito+RelfectionUtilsを使う方法を書きました。ほぼ(3)と同じですが。