17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SpringでDIされるオブジェクトのMockテスト

Last updated at Posted at 2015-04-18

調べてもいろんな方法があったので備忘録としてメモしておく。

かなり適当ですがこんな感じに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を追加。

build.gradle
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を追加。

build.gradle
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)と同じですが。

17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?