2011-05-12 59 views
3

我是新来的单元测试。有看起来像一个方法:你如何为需要很长时间的方法进行单元测试?

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文件来测试此方法,但有时会测试Web版本。

有什么建议吗?

+0

我知道我可以传递一个本地url作为getImage的参数,但这是一个特例。例如,也许我需要使用预先计算的数据来代替测试的慢速算法。 – 2011-05-12 18:19:52

回答

3

您的单元测试不需要验证互联网的工作。您应该提供一些模拟HTML文件并指向那些用于测试的文件。既然你正在处理一个URL,你应该能够指定一个像"file:///path/to/test.html"这样的路径来代替网址。

+0

我想要但是如何?这意味着我必须公开paresHtmlToImage()? – 2011-05-12 18:23:50

+0

我更新了答案,您应该能够指定URL的文件路径。 – WhiteFang34 2011-05-12 18:25:54

+0

看到我对OP的评论。为了方便,我使用了URL示例 – 2011-05-12 18:29:13

1

这里有两个选择 -

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

  2. 在其他时候,当你想从web获取URL时,它会变得不仅仅是单元测试,因为你正在外部移动测试依赖关系。在这种情况下,您必须处理副作用,即网络延迟,资源可用性等。此集成测试的一个优化是在多个测试依赖于该资源时缓存资源。这样你就不需要每次都通过网络进行访问。

+0

我会在1中看到你提到的库。但是我不明白2.是什么意思,我应该在单元测试中测试本地版本,但在集成版本中测试网络版本? – 2011-05-12 18:27:37

+0

是的 - 你说得对。另外,如果你有多个测试测试fetchURL(...)的方法,我建议缓存结果。 – kuriouscoder 2011-05-12 18:39:31

3

简答:依赖倒置。

稍长回答:

问题是可能涉及到什么fetchSource所做的事。没有看到细节,这个答案将不得不有点模糊。我假设fetchSource调用了一些实际执行检索的内容,并且这个过程的详细信息并不是你真正想要测试的内容。我还假设你想在写“fecht”的地方写“抓取”,但对答案并不特别重要。

您想抽象掉fetchSource的具体位,以便您可以专注于测试对您来说重要的事情(parseHtmlToImage?)。有多种方法可以做到这一点:

  • 构造您imageFetcher与存根实现,通过它您实际检索URL的界面,在传递通过构造(反向相关性);你的存根将扮演“互联网”的角色并返回一个罐头响应。
  • 如果您无法创建接口的存根实现,请引入一个适配器接口,其中包含使用当前用于检索的接口的生产实现以及用于测试的存根实现。

这是编写可测试代码的相当标准的技术(查找依赖性反转原理)。

+0

>通过构造函数传入 什么是“它”?网址?该对象实现了存根方法? (你能举几个例子吗?我读了wiki上的DIP,但不知道如何在这种情况下使用它) – 2011-05-12 18:36:46

+0

对不起,延迟;仍然不习惯StackExchange UI来跟踪新的评论。在这种情况下,“它”将是生产代码中接口的生产实现,以及测试代码中的接口的存根实现(在这种情况下,该接口抽象出实际获取URI的细节)。 – 2011-05-16 19:40:07

1

为了扩大对依赖注入的答案以上:

所以,你必须:

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没有这(。对我来说)听起来象是一个不同的“网络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; 
    } 
} 

现在图像撷取我主要关心的是确定要使用哪个图像。它不再需要关心你如何从网络上获取该图像。 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); 
} 
相关问题