您的位置:首页 > 移动开发 > Objective-C

JUnit学习笔记9---mock object进行孤立测试3

2013-04-18 10:39 543 查看
实践HTTP连接示例程序

为了了解mock objects在实际的例子中是如何工作的,书中举了这样的一个例子,它打开了一个远程的Http连接,读取页面的内容,在上面的笔记中使用了stub技术进行了测试,当时也分析了stub的不可取之处,那么今天介绍的mock技术可以弥补!那就再进行一测试吧,假期八天的时间,再我看来就是无情的测试。。。

我们将要学习使用java接口类HttpURLConnection编写mock。书中展示了如何从初始的测试开始,如何进一步的改善,以及如何的修改源代码使之更加的灵活。还有如何使用mock测试错误的情况。 (看不到图的朋友点击这里!)






定义mock object

下图说明了mock object的定义,MockURL类替换了真正的URL类,在getContent中所有对URL类的调用由MockURL类接管。就像你看到的那样,测试一个controller:它创建并配置了测试中mock必须具备的功能,用MockURL类代替了真正的URL类,运行测试。





上面的示例图展示了mock objects策略有趣的一面:在代码中能够换入mock。有心的您会注意到,因为URL类是final类型的,它根本就不能创建出一个扩展的MockURL类。在接下来的节里,我们将展示这个技巧如何是以不同的方式执行的。在任何的案例中,当用到mock objects策略时,把真正的mock换做mock是一个难点。这也许可以看作mock objects的一个缺点,因为通常要修改你的代码,以提供暗门。


打开HTTP连接的代码

package junitbook.fine.try1;

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;
import java.io.IOException;

public class WebClient
{
public String getContent(URL url)
{
StringBuffer content = new StringBuffer();

try
{
HttpURLConnection connection =
(HttpURLConnection)url.openConnection();                connection.setDoInput(true);
InputStream is = connection.getInputStream();

byte[] buffer = new byte[2048];
int count;
while (-1 != (count = is.read(buffer)))
{
content.append(new String(buffer, 0, count));
}
}
catch (IOException e)
{
return null;
}
return content.toString();
}
}



尝试1:简单的方法重构技法

我们的想法是能够不依赖于web服务器的真正的HTTP连接,独立的测试getContent方法。如果你应用在JUnit学习笔记7中的方法,那么就是遍一个mockURL,在其中,url.openConnection方法返回一个mock HttpURLConnection。MockHttpURLConnection类将提供一个测试决定getInputStream方法返回什么的实现。理论上,你能写入下的测试:

public void testGetContentOk() throws Exception
{
MockHttpURLConnection mockConnection =
new MockHttpURLConnection();                        ;创建一个mock HttpURLConnection.建立它是为了在其上调用
mockConnection.setExpectedInputStream(                  ;getInputStream方法时返回包含“It works”的字符流
new ByteArrayInputStream("It works".getBytes()));   ;

MockURL mockURL=new MockURL();                         ;创建一个mock URL也是同一个道理,当url.openConnection
mockURL.setupOpenConnection(mockConnection);  ;被调用时,返回MockURLConnection.

WebClient client=new WebClient();

String result = client.getContent(                       ;调用getContent方法来测试,把你的MockURL实例传给它
mockURL);

assertEquals("It works", result);                        ;确认结果
}


不幸的是,这个方法并没有用!JDK URL类是一个final类,没有任何的URL接口可以用!继承到此为止,你需要找到另外的一个解决方案!很可能是mock另外的一个对象。一个办法就是替换URLStreamHandlerFactory类,在笔记6中研究了这个方法,那么让我们找出一个使用mock objects的办法吧,那就是重构getContent方法。如果你思考一下就会发现,这个方法做了两件事情,它取了HttpURLConnection对象,然后从中读取内容。可以将其重构,结果就是下面的类。

package junitbook.fine.try1;

import java.net.URL;
import java.net.HttpURLConnection;
import java.io.InputStream;
import java.io.IOException;

public class WebClient
{
public String getContent(URL url)
{
StringBuffer content = new StringBuffer();

try
{
HttpURLConnection connection =
createHttpURLConnection(url);
InputStream is = connection.getInputStream();

byte[] buffer = new byte[2048];
int count;
while (-1 != (count = is.read(buffer)))
{
content.append(new String(buffer, 0, count));
}
}
catch (IOException e)
{
return null;
}

return content.toString();
}

protected HttpURLConnection createHttpURLConnection(URL url)
throws IOException
{
return (HttpURLConnection) url.openConnection();
}
}


上面的部分就是重构的结果,你现在调用createHttpURLConnection方法,创建Http连接。

该方法如何使得你测试getContent方法更有效呢?你可以用一个技巧:写一个继承WebClient类的测试辅助类,并且复写它的createHttpURLConnection方法,把它传递给mock HttpURLConnection对象。测试代码如下:

package junitbook.fine.try1;

import java.io.ByteArrayInputStream;
import java.io.IOException;

import java.net.HttpURLConnection;
import java.net.URL;

import junit.framework.TestCase;

public class TestWebClientMock extends TestCase
{
public void testGetContentOk() throws Exception
{
MockHttpURLConnection mockConnection =
new MockHttpURLConnection();
mockConnection.setExpectedInputStream(
new ByteArrayInputStream("It works".getBytes()));

TestableWebClient client = new TestableWebClient();
client.setHttpURLConnection(mockConnection);

String result = client.getContent(         ;getContent方法接收URL作为参数,所以你要传一个参数
new URL("http://localhost"));

assertEquals("It works", result);
}

private class TestableWebClient extends WebClient   ;配置TestableWebClient,以便createHttpURLConnection方法返回一个mock
{                                                   ; object
private HttpURLConnection connection;

public void setHttpURLConnection(
HttpURLConnection connection)
{
this.connection = connection;
}

public HttpURLConnection createHttpURLConnection(URL url)
throws IOException
{
return this.connection;
}
}
}


这是一个常用的重构技巧,叫做Method Factory重构。它在mock的类中没有接口时特别的有用。策略是扩展这个类,加入一些setter方法来控制,覆写一些getter方法来得到测试要的东西。对这个例子而言,这个方法不错,但是也不完美。有点像海森堡的测不准原理:被测的子类改变了它的行为。

在下个笔记中,将记录另外的一种尝试:使用类工厂进行重构!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: