Dojo 敏捷开发:集成 DOH 单元测试到 Ant build (3)
 
- UID
- 1066743
|

Dojo 敏捷开发:集成 DOH 单元测试到 Ant build (3)
集成浏览器运行方式命令行运行方式在很多方面存在着缺陷,例如:无法测试与 DOM 操作相关的代码,无法测试浏览器的兼容性等等。而浏览器运行方式就能够弥补这些缺陷。可是将浏览器运行方式集成到 Ant build 中会相对复杂一些。这种方式需要一个可访问服务器的支持,这个服务器可以是一个部署好的公用服务器,也可以是在 build 中启动的一个本地服务器,下面以本地服务器为例。在 build 中启动一个本地的 Web 服务器可以使用嵌入式的 Web 服务器,例如 Jetty。
在浏览器运行方式中,主入口是一个 html 文件,例如 runner.html。我们可以将该文件放在 WebContent 的根目录中,并将启动的 Web 服务器的 Web 应用根目录设置为 WebContent,这样就可以通过 http://localhost/runner.html 访问。
所以在 Ant build 中启动单元测试就变得简单了,只需要启动一个浏览器并且访问该地址即可,这里的浏览器可以是任何想要测试的浏览器,也可以将每种浏览器测试一遍。以 Firefox 为例,Ant 片段如清单 5 所示。
清单 5. Ant 中启动浏览器1
2
3
4
5
| <target name="runUT">
<exec executable=" C:\Program Files\Mozilla Firefox\firefox.exe">
<arg value="http://localhost/runner.html"/>
</exec>
</target>
|
其中,executable 是浏览器应用程序可执行文件或命令的路径,arg 是访问 runner.html 的 web 路径。这条命令可以达到启动单元测试的目的,但是当浏览器被启动起来以后这条命令就直接返回了,所以 Ant build 可能在单元测试执行结束之前就已经返回了,无法得到单元测试的执行结果。所以我们需要一个办法让 Ant build 等待单元测试执行结束。这时候可以借助 Web 服务器来传递这个消息。
我们可以实现一个服务器端的消息转接服务,该服务可以通过 HTTP 写入或者读取单元测试的执行结果。在 runner.html 中添加一段 JavaScript 代码用于在单元测试执行结束后将结果通过 Ajax 方式写入到服务中。除此之外,我们还需要实现一个 Java 可执行类,该类以一定间隔从服务器端的服务中去取执行结果,当无法取得执行结果时等待一定间隔重试,直到取得执行结果,然后输出执行结果并且根据执行结果是否成功决定返回结果。在 Ant build 中,当单元测试被启动后启动该 Java 可执行类。通过这个方式我们可以让 Ant build 等待单元测试执行结束并且将单元测试的结果输出,当单元测试失败时让 build 失败。
- 编写消息转接服务 消息转接服务是一个简单的服务器端处理单元,以什么方式实现取决于项目的服务器端技术,可以是 Servlet、REST executor 等等。下面以 REST executor 为例实现该处理单元。
我们需要将写入和读取的 HTTP 请求都定向到这个处理单元中。写入的 HTTP 请求附带 HTTP 消息体,读取的 HTTP 请求没有 HTTP 消息体。所以该处理单元可以通过是否有 HTTP 消息体来判断是写入请求还是读取请求。实现代码如清单 6。
清单 6. 消息转接服务代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class ResultService extends ReqExecutor {
private String result = "";
public ReqExecutor newInstance() {
return this ;
}
public HTTPResponse execute(HTTPRequest req) {
String body = req.getBody();
if(body != null && !body.trim().equals("")) result = body;
else {
return new StringResponse(result);
}
}
}
|
为了保证写入和读取的 HTTP 请求都由同一个 ResultService 对象进行处理,所以这里当 newInstance 时就返回 this。
将该消息注册到 REST framework 中,并为其指定一个不与其它服务冲突的 URL,例如: ”/resultService”。
- 修改 DOH 的执行代码 在 DOH 中,runner.js 中有一个 doh._report 方法负责在单元测试执行结束后汇报执行结果。我们可以修改该方法,让它在所有测试执行结束后把运行结果通过 Ajax 方式发送给服务器端的消息转接服务。修改后的方法体如清单 7。
清单 7. 修改后的 doh._report 方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| doh._report = function (){
this .debug("| TEST SUMMARY:");
this .debug(this ._line);
this .debug("\t", this ._testCount, "tests in",
this ._groupCount, "groups");
this .debug("\t", this ._errorCount, "errors");
this .debug("\t", this ._failureCount, "failures");
//---------------------------------------------------------------------------
// 定义 request 对象,把需要写入 build 的信息传递给 server 端
var request = {
//url 为用于接收参数的 rest excutor
url: "http://localhost/resultService",
handleAs: "text",
preventCache: true ,
content :
// 总 test case 数量
"testCount: " + this ._testCount + "\n" +
// 总 test suit 数量
"groupCount: " + this ._groupCount + "\n" +
// 出错数量
"errorCount: " + this ._errorCount + "\n" +
// 失败数量
"failureCount: " + this ._failureCount + "\n" +
// 全部执行过程中的 log 内容
"logBody: " + dojo.byId("logBody").innerHTML +
// 执行结果是成功或者失败,将被 Ant build 用于决定是否让 build 失败
this ._errorCount + this ._failureCount > 0 ? "successful" : "failed",
load: function (data, ioargs){
console.log("ok");
},
error: function (error){
console.log("error");
}
};
// 向 server 端发送 POST 请求
dojo.xhrPost(request);
}
|
方法体中分割线以上部分为原方法体,以下部分为增加的代码。在该方法中可以分别通过内部变量或者 innerHTML 获得所有已执行单元测试的统计信息(比如:test case 的数量、test suit 的数量)和执行结果(比如:出错数量、失败数量、执行的 log 内容)。在以上的样例实现中,只是简单的将所有这些内容以纯文字的形式返回给消息转接服务。如果不希望这样,当然也可以选择其它的方式(例如 JSON)和内容,但是这样也同时需要修改消息转接服务和 Ant build 中的 Java 可执行类。
- 集成 Java 可执行类到 Ant build 这个 Java 可执行类需要提交 HTTP 请求到 http://localhost/resultService,并且获取消息转接服务所保存的执行结果。代码如清单 8。
清单 8. Java 可执行类示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
public class ResultRequest {
public static void main(String[] args) throws IOException {
URL u = new URL("http://localhost/resultService");
//int tryCounts = 0;
while (true ){
//if(tryCounts > 60) System.exit(1);
//tryCounts++;
InputStream inStream = u.openStream();
String result = readContentFromStream(inStream);
if (result != null && !result.trim().equals("")) {
System.out.println(result);
if (result.endsWith("failed")) System.exit(1);
else return ;
} else {
Thread.sleep(1000);
}
}
}
}
|
该类打开连接消息转接服务的 URL,获得 URL 所返回的内容,如果返回的内容为空则继续等待,等待时间可以设置为一个合适的时间,比如 1 秒钟;如果返回的内容不为空则说明已经取到单元测试的运行结果,然后输出取到的结果并且从结果中获得单元测试是否成功,如果不成功则以错误代码返回,否则直接返回。
将该类集成到 Ant build 中,首先将该类编译打包,然后放在一个 build 脚本可以访问的库目录中,例如 WebContent/lib。Ant 片段如清单 9。
清单 9. 集成 Java 可执行类到 Ant1
2
3
| <target name="testResult" depends="runUT">
<java classname="ResultRequest" classpath="lib" failonerror=”true”/>
</target>
|
该 target 在 runUT target 后执行,所包含的工作就是执行这个 Java 类。
|
|
|
|
|
|