转载

使用Groovy构建自己的脚本环境

场景

在进行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
正文到此结束
Loading...