【问题标题】:Spock mock returns null inside collabolator but not in feature methodSpock 模拟在协作者内部返回 null,但在功能方法中不返回
【发布时间】:2013-07-01 03:38:27
【问题描述】:

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
        }

    }
}

现在,在“测试执行”中,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() 然后阻塞

【问题讨论】:

  • 有几件事看起来很奇怪,但主要问题是您没有在期望中重复存根规范的 >> 部分(即,然后阻塞)。见
  • 也许我没有很好地说明我的问题 - 我不想只检查是否调用了 getFileNames。我需要结果来正确检查 for 循环。如果我在执行中注释循环,则测试通过。但是由于我使用 getFilenames() 方法的结果,我得到一个 NPE 执行方法到达 for 循环。

标签: junit mocking spock


【解决方案1】:

主要问题是您没有在期望中包含响应生成器(>> 部分)(即 then: 块内的“1 * ...”部分)。

这在 spock 文档中有很好的解释。

您不必在 setup: 块中声明您的存根。您可以只在 then: 块中指定一次——即使在调用 receiveData.execute() 之后也是如此。由于 Groovy AST 转换,这就是 spock 的魔力的一部分。而且由于(非共享)字段在每次测试之前都会重新初始化(更多基于 AST 的魔法),在这种情况下您甚至不需要 setup()。

另一件奇怪的事情是,您既要存根 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
    }
}

【讨论】:

  • 好的,我再次仔细阅读了您指定的文档,确实测试运行正确,我得到了 execute() 中的值。谢谢!
  • “这在 spock 文档中有很好的解释。”当然可以,但你的例子更好。
  • @jeremyjjbrown 同意。我会说 Spock 文档根本没有很好地解释这一点。
  • 完美解决不应该存在的问题(多么奇怪的设计决定?!)。
猜你喜欢
  • 2011-09-30
  • 2020-01-21
  • 1970-01-01
  • 2018-10-09
  • 2020-01-18
  • 1970-01-01
  • 2022-12-09
  • 1970-01-01
相关资源
最近更新 更多