【问题标题】:Mock return of chained method with spock使用 spock 模拟返回链式方法
【发布时间】:2018-10-09 05:07:06
【问题描述】:

我已经被这个问题困住了一段时间。是否可以模拟 new URL(url).openStream() 以返回 file.gz?我正在使用 spock 来尝试这样做。

public class DownloadFile {
public  BufferedReader downloadGzipCsvFile(String url) throws MalformedURLException {
    BufferedReader br = null;
    if (UrlValidator.getInstance().isValid(url)){
        try {
            br = new BufferedReader(new InputStreamReader(new GZIPInputStream(new URL(url).openStream())));
        } catch (IOException e) {
            e.printStackTrace();
            System.exit(0);
        }
    } else{
        throw new MalformedURLException("Supplied URL: " + url + " is an invalid URL");
    }
    return br;
}
}

测试类

class DownloadFileSpec extends Specification{

def "Should return buffered reader for url for gzip csv file"(){
    given:
    String url = "http://www.test.com"
    DownloadFile downloadFile = new DownloadFile()

    when:
    BufferedReader br = downloadFile.downloadGzipCsvFile(url)
    _.openStream() << new FileInputStream("../../../../resources/test_data.csv.gz")

    then:
    br.ready()
}
}

或者我会更好地编写一个返回流的私有方法?

【问题讨论】:

  • 你为什么不建立一个服务器,并将它的 URL 传递给方法?

标签: java unit-testing groovy spock


【解决方案1】:

可测试性的问题几乎总是相同的:没有依赖注入。这个案子也不例外。这里的具体问题是,您不能简单地提供 file://... URL,因为您使用 Apache Commons Validate,它为 isValid(url) 生成 false

解决方案是使您的类能够通过 setter 或其他构造函数注入 UrlValidator。我在我的示例代码中选择了 setter 方法。如果您不喜欢公共设置器,我什至将设置器设置为包范围,因此您可以将测试放入与原始类相同的包中(无论如何这是常见的做法)以便访问它。

现在只需在测试期间注入一个 mock 并愉快地使用文件 URL:

package de.scrum_master.stackoverflow;

import org.apache.commons.validator.routines.UrlValidator;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.zip.GZIPInputStream;

public class DownloadFile {
  private UrlValidator urlValidator = UrlValidator.getInstance();

  void setUrlValidator(UrlValidator urlValidator) {
    this.urlValidator = urlValidator;
  }

  public BufferedReader downloadGzipCsvFile(String url) throws MalformedURLException {
    BufferedReader br = null;
    if (urlValidator.isValid(url)) {
      try {
        br = new BufferedReader(
          new InputStreamReader(
            new GZIPInputStream(
              new URL(url).openStream()
            )
          )
        );
      } catch (IOException e) {
        e.printStackTrace();
        System.exit(0);
      }
    }
    else {
      throw new MalformedURLException("Supplied URL: " + url + " is an invalid URL");
    }
    return br;
  }
}
package de.scrum_master.stackoverflow

import org.apache.commons.validator.routines.UrlValidator
import spock.lang.Specification

class DownloadFileSpec extends Specification {
  def "Should return buffered reader for url for gzip csv file"() {
    given:
    String url = new File("src/test/resources/test_data.csv.gz").toURI().toURL()
    def downloadFile = new DownloadFile()
    downloadFile.urlValidator = Mock(UrlValidator) {
      isValid(_) >> true
    }

    when:
    def bufferedReader = downloadFile.downloadGzipCsvFile(url)

    then:
    bufferedReader.ready()
  }
}

顺便说一句,该测试还展示了如何将从相对路径创建的File 实例转换为文件 URL。

P.S.:您还可以使用 PowerMock 来模拟静态方法或构造函数,这也可以解决问题。但我相信,每当您需要 PowerMock 时,它就是代码异味,您应该重构,这就是我为您所做的。

【讨论】:

  • 您是否必须使用 byte-buddy-1.6.4 或 cglib-nodep-3.2 才能使 UrlValidator 的模拟工作?
  • 当然你需要CGLIB来模拟类,没有CGLIB只能模拟接口。不过,Bytebuddy 不是必需的。
猜你喜欢
  • 2021-09-27
  • 1970-01-01
  • 1970-01-01
  • 2014-12-31
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多