场景

在进行Web服务端开发的时候,发布前通常需要测试一遍。对于一个大一点的项目,最好的办法是写个自动化测试程序。

以Groovy为例,写测试代码之前通常的有如下几个操作

  1. 引用相关的类库
  2. import相关的类
  3. 对库不熟悉的时候你很可能得先把库的文档好好看一遍

对于你来说,你需要的可能仅仅是post,get等几个简单的操作而已,而上面的操作更是可能占用你整个开发过程的大部分时间。

Orz....项目进度没跟上,又要加班了。。。。

要是有一种语言,本身自带post,get这样的函数那该多好啊,测试程序哗啦哗啦就写完了!

解决方案

通过Groovy构建一个脚本环境,自带post,get这些常用的函数

效果

再也不用手动引用库了,再也不用手动import类了,再也不用复习好长好长的文档了,写测试脚本再也腰不疼、腿不麻了!

原理

groovy本身是一个强大的脚本引擎,同时也是高度可定制化的。

在groovy编译脚本的时候,可以为脚本指定一个基类,这个基类的所有方法和属性都可以直接在脚本引用。

实现

首先,先新建一个工程,这里用gradle作为构建工具,取工程名为httpbatch

新建build.gradle

apply plugin: 'groovy'
apply plugin: 'eclipse'
apply plugin: 'application'
mainClassName = 'com.kasonyang.httpbatch.Application'
repositories {
mavenCentral()
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.3.10'
compile 'org.apache.httpcomponents:httpclient:4.5'
}

工程引用了apache的httpclient组件,并指定主类为com.kasonyang.httpbatch.Application

生成Eclipse工程

$ gradle eclipse

然后我们就可以使用Eclipse导入工程了。

创建主类Application.groovy

package com.kasonyang.httpbatch
import com.kasonyang.httpbatch.test.TestException
import org.codehaus.groovy.control.CompilerConfiguration
class Application { static void printUsage(){
println """
usage : httpbatch file #execute a file
or httpbatch -s #enter shell mode
"""
} static void main(String[] args) {
if(args.length<1){
printUsage()
return
}
def reader,scriptStr = ''
switch(args[0]){
case '-s':
print ">"
reader = System.in
reader.eachLine {it->
scriptStr += it + '\n'
if(it == ''){
runScript(scriptStr)
scriptStr = ''
}//else{
print '>'
//}
}
break
default:
def file = new File(args[0])
runScript(file)
break
}
} private static String getExceptionStack(Exception ex,String clsName){
String msg = ""
for(stack in ex.stackTrace){
if(stack.className == clsName){
def fileName = stack.fileName
def lineNumber = stack.lineNumber.toString()
msg += ("\tFile:${fileName}(${lineNumber})")
}
}
msg
} private static runScript(def it){
def config = new CompilerConfiguration()
config.scriptBaseClass = 'com.kasonyang.httpbatch.HttpBatchScript'
def shell = new GroovyShell(config)
def scriptClass
try{
//shell.evaluate(file)
def script = shell.parse(it)
scriptClass = script.class.name
def stateReturn = script.run()
//System.out.println(stateReturn)
}catch(TestException ex){
println "test fail:"
println getExceptionStack(ex,scriptClass)
println "\texcepted:${ex.excepted}\n\tactual:${ex.actual}"
}catch(Exception ex){
println ex.message
println getExceptionStack(ex,scriptClass)
}catch(RuntimeException ex){
println ex.message
println getExceptionStack(ex,scriptClass)
}
}
}

Application指定了脚本的基类为com.kasonyang.httpbatch.HttpBatchScript,这个类就是主题的主体。

先上代码

package com.kasonyang.httpbatch
import com.kasonyang.httpbatch.test.TestException
import com.kasonyang.httprequest.HttpRequest
import com.kasonyang.httprequest.HttpResponse
import groovy.lang.Binding
import groovy.lang.Script;
abstract class HttpBatchScript extends Script { private http = new HttpRequest();
HttpResponse $$ Closure beforeGo,beforePost,afterGo,afterPost Closure testFail private String base = ''
private String path = '' private String trimPath(String path,boolean left=true,boolean right=true){
int start =(left && path.startsWith('/')) ? 1 : 0;
int end = (right && path.endsWith('/')) ? path.length()-1 : path.length();
path.substring(start,end)
} private def getUri(uri){
base + (base?'/':'') + path + (path?'/':'') + trimPath(uri,true,false)
} def base(){
this.base = ''
} /**
* set the base path of request
* @param path the base path
* @return
*/
def base(String path){
this.base = trimPath(path)
} def enter(){
this.path = ''
} /**
* enter a directory in base
* @param path
* @return
*/
def enter(String path){
this.path = trimPath(path)
} /**
* submit a get request
* @param uri the request uri
* @param params the query params
* @param callback call after request
* @return
*/
HttpResponse go(String uri,Map params,Closure callback){
def httpGet = http.createGet(getUri(uri),params)
this.beforeGo?.call(httpGet)
def response = http.execute(httpGet)
this.$$ = response
if(callback) callback.call()
this.afterGo?.call(response)
return this.$$
}
HttpResponse go(String uri,Closure callback){
return go(uri,[:],callback)
}
HttpResponse go(String uri,Map params){
return go(uri,params,null)
}
HttpResponse go(String uri){
return this.go(uri,null)
} /**
* submit a post request
* @param uri the request uri
* @param params the post params
* @param callback call after request
* @return
*/
HttpResponse post(String uri,Map params,Closure callback){
def httpPost = http.createPost(getUri(uri),params)
this.beforePost?.call(httpPost)
def response = http.execute(httpPost)
this.$$ = response
if(callback) callback.call()
this.afterPost?.call(response)
return this.$$
}
HttpResponse post(String uri,Closure callback){
return post(uri,[:],callback)
}
HttpResponse post(String uri,Map params){
return post(uri,params,null)
}
HttpResponse post(String uri){
return this.post(uri,null)
} /**
* set the beforeGo callback,which whill be call before every get request
* @param callback
*/
void beforeGo(Closure callback){
this.beforeGo = callback
} /**
* set the beforePost callback,which whill be call before every post request
* @param callback
*/
void beforePost(Closure callback){
this.beforePost = callback
} /**
* set the callback,which whill be call when test fail
* @param cb
*/
void testFail(Closure cb){
this.testFail = cb
} /**
* set the callback,which whill be call after every get request
* @param callback
*/
void afterGo(Closure callback){
this.afterGo = callback
} /**
* set the callback,which whill be call after every post request
* @param callback
*/
void afterPost(Closure callback){
this.afterPost = callback
} /**
* test whether it is true
* @param value
*/
void testTrue(Object value){
testEquals(true,value)
} /**
* test whether actual equals the excepted
* @param excepted
* @param actual
*/
void testEquals(Object excepted,Object actual){
if(excepted != actual){
def ex = new TestException(excepted,actual)
if(this.testFail){
testFail(ex)
}else{
throw ex
}
}
} /**
* test whether it is null
* @param value
*/
void testNull(Object value){
testEquals(null,value)
} }

这个类主要定义了一个public属性$$还有几个public方法,也就是post、go,和一些其它可能需要用到的函数。

因为get方法在groovy里有特殊意义,这里使用go方法名代替了get。

提示:这里使用了另外两个类,HttpRequest和HttpResponse,是自定义的两个Class,由于篇幅的原因,这里就不再贴代码了,具体实现可前往Github查看。

我已经把全部源码放到了Github,感兴趣的可以前往查看。

地址:https://github.com/kasonyang/httpbatch

构建项目

$ gradle installDist

程序被输出到build/install/httpbatch目录下,将bin目录添加到环境变量PATH中。

使用

  • 创建脚本文件 "example.hb"
go "YOU_URL"//对你要测试的URL提交get请求
testEquals 200,$$.statusCode//状态码为 200?
def text = $$.text //get the response as text
def json = $$.json//get the response as json
println text //output the response
//这里添加你的测试逻辑代码
println "Test successfully!"
  • 执行脚本文件
$ httpbatch example.hb

使用Groovy构建自己的脚本环境的更多相关文章

  1. Spark:利用Eclipse构建Spark集成开发环境

    前一篇文章“Apache Spark学习:将Spark部署到Hadoop 2.2.0上”介绍了如何使用Maven编译生成可直接运行在Hadoop 2.2.0上的Spark jar包,而本文则在此基础上 ...

  2. 为 Python Server Pages 和 Oracle 构建快速 Web 开发环境。

    为 Python Server Pages 和 Oracle 构建快速 Web 开发环境. - 在水一方 - 博客频道 - CSDN.NET 为 Python Server Pages 和 Oracl ...

  3. 使用Eclipse+Maven+Jetty构建Java Web开发环境(几个教程综合集成2014发行)

    工作需要使用Jetty由于web集装箱,得知Eclipse+Maven+Jetty该组合是非常好的,因此,要在网上找了很多教程,但不写或多或少特定的或过时的内容而导致最终的配置失败,易于配置为未来的同 ...

  4. [phvia/dkc] Docker Compose 快速构建(LNMP+Node)运行环境

    快速构建(LNMP+Node)运行环境. dkc 在此作为 docker-compose 的缩写,你可以理解为 alias dkc=docker-compose 准备 安装 docker 选择1) 从 ...

  5. [转]构建Python+Selenium2自动化测试环境(二)

    构建Python+Selenium2自动化测试环境完成之后,就需要测试支持python的selenium的版本是否都支持在不同浏览器上运行,当前我们分别在三个最通用的浏览器上通过脚本来测试. 1.在I ...

  6. [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux)

    [.net 面向对象程序设计深入](5)MVC 6 —— 构建跨平台.NET开发环境(Windows/Mac OS X/Linux) 1.关于跨平台 上篇中介绍了MVC的发展历程,说到ASP.NET ...

  7. 快速构建springmvc+spring+swagger2环境

    快速构建springmvc+spring+swagger2环境 开发工具:Intellij idea               jdk: 1.8 开发步骤: 1.创建maven工程,如图建立工程结构 ...

  8. Linux计划任务 定时任务 Crond 配置详解 crond计划任务调试 sh -x 详解 JAVA脚本环境变量定义

    一.Crond 是什么?(概述) crontab 是一款linux系统中的定时任务软件用于实现无人值守或后台定期执行及循环执行任务的脚本程序,在企业中使用的非常广泛.     现在开始学习linux计 ...

  9. Android NDK开发 Android Studio使用新的Gradle构建工具配置NDK环境(一)

    本文主要讲述了如何如何在Android Studio使用新的Gradle构建工具配置NDK环境,现在把相关的步骤整理出来分享给Android程序员兄弟们,希望给他们在配置NDK环境时带来帮助. 从An ...

随机推荐

  1. ArcGIS10.2最新全套下载地址

    http://www.tuicool.com/articles/VfaMfy 免责声明: 该链接来自于哥伦比亚大学或者牛津大学的网站链接, 下载 软件之前确保有正版的软件授权 ,本博客只是转载了网站链 ...

  2. import的用法

    转自python学习笔记--模块和命名空间 模块(module)是Python中非常重要的一个概念,模块其实就一些函数和类的集合文件,它能实现一些相应的功能,当我们需要使用这些功能的时候,直接把相应的 ...

  3. hibernate 问题

    如果hibernate中反转的表中没有主键的话,会生产三个文件. table.java tableADO.java tableId.java 并且在执行findByProperty时,会提示:coul ...

  4. Windows server 2008R2部署服务批量安装Windows7教程

    利用Windows server 2008 R2下的Windows部署服务可以批量安装Windows 7,以下简称WDS. WDS需要用到域和dhcp.DNS服务,所以,基础环境必须要有域控制器,dh ...

  5. win7启动文件修复

    1:在xp下运行bcdedit.exe(这软件在windows 7的系统盘下 就在你的c:\windows\system32\这里2:将这软件复制到c盘根目录下3:进入命令提示符 输入 c:4:输入 ...

  6. static local variable

    Putting the keyword static in front of a local variable declaration creates a special type of variab ...

  7. 已经导入了具有相同的简单名称“Interop.DSOFramer, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null”的程序集。

    错误  : 已经导入了具有相同的简单名称“Interop.DSOFramer, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null”的程序集. ...

  8. arcgis api for javascript 3.16开发(一)

    原来一直都在用Flex开发arcgis的地图接口,用的时间很长,用的习惯也顺手,可Flex这个开发工具已经基本要淘汰了,并且地图借助flash的方式加载在浏览器里已经不能适应webgis的快速开发需求 ...

  9. 通过HttpClient方式连接网络

    xml: <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:t ...

  10. Keil(MDK-ARM)使用教程(三)_在线调试

    Ⅰ.概述 该文章总结Keil(MDK-ARM)在线调试相关的内容,详情请往下看. 该文章是基于新建好软件工程来讲述,关于Keil的下载.安装和新建工程我已将在前面做了详细的总结,不懂的可以参看我博客里 ...