spock + mockitoでverifyするときの注意点

mockitoでverifyするときにテストが通らなくてハマったのでメモ

サンプルコード

プロダクト

public class Sample {
    public interface Repository {
        int insert(String data);
    }

    @AllArgsConstructor
    public static class Main {
        private final Repository repository;

        public void run() {
            repository.insert("hello world");
        }
    }
}

Repository#insert()は何かデータを挿入して、結果としてIDを返却するようなメソッドです
そのメソッドをMain#run()で呼び出しています

テスト

Main#run()の中でRepository#insert()を正しく呼び出せているかをテストします

class SampleTest extends Specification {
    def "test"() {
        setup:
        def mockRepository = Mockito.mock(Sample.Repository)
        def sut = new Sample.Main(mockRepository)

        when:
        sut.run()

        then:
        Mockito.verify(mockRepository, Mockito.times(1)).insert(Mockito.any(String))
    }
}

これで実行したらうまく通りそうですが、実行するとこうなります

SampleTest > test FAILED
    Condition not satisfied:

    Mockito.verify(mockRepository, Mockito.times(1)).insert(Mockito.any(String))
            |      |                       |         |              |
            |      |                       |         0              null
            |      |                       Wanted invocations count: 1
            |      Mock for Repository, hashCode: 2069833259
            Mock for Repository, hashCode: 2069833259
        at util.SampleTest.test(SampleTest.groovy:18)

なんでや!

テストが通らない理由

テストが通らない理由はテストのthenのところ

then:
        Mockito.verify(mockRepository, Mockito.times(1)).insert(Mockito.any(String))

ここでinsertが0を返します
thenの中では各行が評価されますが、行の結果が0の場合はテスト失敗とみなされます
だから通らない

結論: 戻り値のあるメソッドをverifyするときは戻り値を評価しろ

thenをこう書くべきです

then:
        Mockito.verify(mockRepository, Mockito.times(1)).insert(Mockito.any(String)) == 0

まとめ

spockのテストでMockito.verify()が通らなくてハマりました
原因は戻り値を評価していないこと
なので戻り値のあるメソッドをverifyするときは戻り値を評価しろ!