5

Spock Mock() オブジェクトに問題があります。テストしようとしているJavaクラスがあります。このクラスは、モックしたいいくつかの ftp 処理を行います。私のサンプルコード

class ReceiveDataTest extends Specification{
    String downloadPath = 'downloadPath';
    String downloadRegex = 'downloadRegex';
    SftpUtils sftpUtils = Mock();
    ReceiveData receiveData;

    def setup(){
        sftpUtils.getFileNames(downloadPath,downloadRegex) >> ['file1', 'file2']
        receiveData= new ReceiveData()
        receiveData.setDownloadPath(downloadPath)
        receiveData.setDownloadRegex(downloadRegex)
        receiveData.setSftpUtils(sftpUtils);

    }

    def "test execute"() {
        given:
        def files = sftpUtils.getFileNames(downloadPath,downloadRegex)
        files.each{println it}
        when:
        receiveData.execute();
        then:
        1*sftpUtils.getFileNames(downloadPath,downloadRegex)
    }

}

public class ReceiveData(){

  //fields, setters etc

    public void execute() {
        List<String> fileNames = sftpUtils.getFileNames(downloadPath, downloadRegex);

        for (String name : fileNames) {
            //dowload and process logic
        }

    }
}

ここで、"test execute" 内で files.each{} が期待される内容を出力します。しかし、 receiveData.execute() が呼び出されると、私の sftpUtils は null を返します..理由はありますか?

編集 多分私は自分の問題をうまく述べていませんでした-getFileNamesが呼び出されたかどうかを確認したくないだけです。forループを適切にチェックするには、結果が必要です。実行内のループにコメントを付けると、テストに合格します。しかし、getFilenames() メソッドの結果を使用するため、NPE 実行メソッドが for ループに到達します。mockitoを使用すると、このようなことをします

Mockito.when(sftpUtils.getFilenames(downloadPath, downloadRegex)).thenReturn(filenamesList);
receiveData.execute();

Mockito.verify(sftpUtils).getFilenames(downloadPath, downloadRegex);
//this is what I want to test and resides inside for loop
Mockito.verify(sftpUtils).download(downloadPath, filenamesList.get(0));
Mockito.verify(sftpUtils).delete(downloadPath, filenamesList.get(0));

しかし、Spock 内で Mockito.verify() を使用してブロックすることはできません

4

1 に答える 1

15

主な問題は、レスポンス ジェネレーター (>> 部分) を期待値 (then: ブロック内の "1 * ..." 部分) に含めていないことです。

これは spock のドキュメントでよく説明されています。

setup: ブロックでスタブを宣言する必要はありません。then: ブロックで 1 回だけ指定できます。これは、Groovy AST 変換による spock の魔法の一部です。また、(共有されていない) フィールドは各テストの前に再初期化されるため (より AST ベースのマジック)、この場合は setup() も必要ありません。

もう 1 つの奇妙な点は、sftpUtils.getFilenames() をスタブ化し、テスト コードから呼び出していることです。モックとスタブは、テスト中のシステムから呼び出される共同作業者を置き換えることを目的としています。テスト ドライバーからスタブを呼び出す理由はありません。そのため、指定されたブロックから getFilenames() への呼び出しを削除し、代わりにテスト対象のコードに呼び出しさせます (そうであるように)。

Groovy を使用すると、Java の set メソッドと get メソッドの呼び出しを簡素化できます。以下の receiveData の初期化を見てください。Groovy で def を使用しても問題ありません。コンパイラにデータ型を判断させます。

次のようなものにつながります:

class ReceiveDataTest extends Specification {

    // only use static for constants with spock
    static String PATH = 'downloadPath'
    static String REGEX = 'downloadRegex'

    def mockSftpUtils = Mock(SftpUtils)

    def receiveData = new ReceiveData(downloadPath : PATH,
                                      downloadRegex : REGEX,
                                      sftpUtils : mockSftpUtils)

    def "execute() calls getFileNames() exactly once"() {
       when:
           receiveData.execute()
       then:
            1 * mockSftpUtils.getFileNames(PATH, REGEX) >> ['file1', 'file2']
            0 * mockSftpUtils.getFileNames(_,_) 

           // The second line asserts that getFileNames() is never called
           // with any arguments other than PATH and REGEX, aka strict mocking
           // Order matters! If you swap the lines, the more specific rule would never match
    }
}
于 2013-07-03T16:48:54.613 に答える