【问题标题】:How do you unit test for methods that take a long time?您如何对需要很长时间的方法进行单元测试?
【发布时间】:2011-05-12 18:09:20
【问题描述】:

我是单元测试的新手。有一个方法看起来像:

public Image getImage(String url) {
    Document pageSource = fecthSource(url);
    Image myImage = parseHtmlToImage(pageSource);
    return Image;
}

我写了一个单元测试:

@test
public void getRightPicture() {
    Image img = imageFetcher.getImage("http://www.123.com");
    assertEquals(img.sourceUrl, "http://www.123.com/456.png");
    Image img = imageFetcher.getImage("http://www.abc.com");
    assertEquals(img.sourceUrl, "http://www.abc.com/def.png");
}

但是如果访问互联网需要很长时间。我通常想使用本地 HTML 文件来测试这种方法,但有时会测试网络版本。

有什么建议吗?

【问题讨论】:

  • 我知道我可以将本地 url 作为 getImage 的参数传递,但这是一种特殊情况。例如,也许我需要使用预先计算好的数据来代替慢速算法来进行测试。

标签: java eclipse unit-testing


【解决方案1】:

您的单元测试不需要验证互联网是否正常工作。您应该提供一些模拟 HTML 文件并指向这些文件进行测试。由于您处理的是 URL,因此您应该能够指定类似 "file:///path/to/test.html" 的路径来代替 Web URL。

【讨论】:

  • 我想要,但是怎么做?这意味着我必须将 paresHtmlToImage() 公开?
  • 我更新了答案,你应该可以为 URL 指定一个文件路径。
  • 查看我对 OP 的评论。为方便起见,我使用 URL 示例
【解决方案2】:

简答:依赖倒置。

答案有点长:

这个问题大概和 fetchSource 在做什么有关;在没有看到细节的情况下,这个答案必须有点模糊。我假设 fetchSource 调用某些东西来实际执行检索,并且如何完成的细节并不是您真正感兴趣的测试。我还假设您打算在写“fecht”的任何地方都写“fetch”,但这对答案并不是特别重要。

您希望抽象出 fetchSource 的具体部分,以便您可以专注于测试对您来说重要的内容(parseHtmlToImage?)。有多种方法可以做到这一点:

  • 使用您实际检索 URL 的接口的存根实现构造您的 imageFetcher,并通过构造函数将其传入(反转依赖关系);您的存根扮演“互联网”的角色并返回预设响应。
  • 如果您无法创建接口的存根实现,请引入一个适配器接口,其中您有一个生产实现,该接口使用您用于检索的当前接口,以及一个用于测试的存根实现。

这是编写可测试代码的一种非常标准的技术(查看依赖倒置原则)。

【讨论】:

  • >通过构造函数传入 什么是“它”?网址?对象实现存根方法? (你能给我一些示例代码吗?我在wiki上阅读了DIP,但不知道在这种情况下如何使用它)
  • 抱歉耽搁了;仍然不习惯 StackExchange UI 来跟踪新的 cmets。在这种情况下,“它”将是生产代码中接口的生产实现,以及测试代码中接口的存根实现(在这种情况下,接口抽象出实际获取 URI 的细节)。
【解决方案3】:

这里有两个选项-

  1. 本地文件当然是一种更快的处理方式;此外,您可以做的是创建一个模拟对象(请参阅 Mockito 或 Easymock 以促进模拟),以便它可以模拟您尝试测试的类中的 fetchURL 方法;

  2. 1234563在这种情况下,您必须处理副作用,即网络延迟、资源可用性等。此集成测试的一种优化是在多个测试依赖于资源时缓存资源。这样您就不需要每次都通过网络获取。

【讨论】:

  • 我会看到你在1中提到的lib。但我不明白2。你的意思是我应该在单元测试中测试本地版本,但在集成版本中测试网络版本?
  • 是的 - 你是对的。此外,如果您有多个测试测试具有 fetchURL(...) 的方法,我建议您缓存结果。
【解决方案4】:

扩展上面的依赖注入答案:

所以你有:

class ImageFetcher {
    public Image getImage(String url) {
       Document pageSource = fetchSource(url);
       Image myImage = parseHtmlToImage(pageSource);
       return Image;
    }
    private Document fetchSource(String url) {
        // ... Network IO etc
    }
    private Image parseHtmlToImage(Document pageSource) {
        // Parsing logic
        // Network IO
        return image;
    }
}

第一个问题是“这一切真的属于“ImageFetcher”吗?IMO fetchSource 不属于。这(对我来说)听起来像是不同的“Web Utils”类会做的事情。我也会移动获取图片从 ImageFetcher 到 utils 类。

class ImageFetcher {
    private WebUtils webUtils; 
    public ImageFetcher(webUtils webUtils) {
      this.webUtils = webUtils;
    }


    public Image getImage(String url) {
       Document pageSource = webUtils.fetchSource(url);
       Image myImage = parseHtmlToImage(pageSource);
       return Image;
    }

    private Image parseHtmlToImage(Document pageSource) {
        imageURL = // Parsing logic
        image = webUtils.fetchImage(imageURL);
        return image;
    }
}

现在 Image Fetcher 主要关心的是确定要使用哪个图像。它不再需要关心您如何从网络中获取该图像。 WebUtils 可以模拟单元测试。

@test
public void getImage_ReturnsBannerImage() {
     Image expected = // ...


     String html = "<html><img src="fred"/>...</html>"; // Keep this short and simple.
     mockedWebUtil.fetchSource(Any.String).returns(html);
     mockedWebUtil.fetchImage("fred").returns(expected);

     var subject = new ImageFetcher(mockedWebUtil);
     var actual = subject.getImage("blah");

     Assert.AreEqual(expected, actual);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 2020-03-18
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 2017-01-08
    相关资源
    最近更新 更多