使用Groovy構建本身的腳本環境

場景

在進行Web服務端開發的時候,發佈前一般須要測試一遍。對於一個大一點的項目,最好的辦法是寫個自動化測試程序。
以Groovy爲例,寫測試代碼以前一般的有以下幾個操做git

  1. 引用相關的類庫
  2. import相關的類
  3. 對庫不熟悉的時候你極可能得先把庫的文檔好好看一遍

對於你來講,你須要的可能僅僅是post,get等幾個簡單的操做而已,而上面的操做更是可能佔用你整個開發過程的大部分時間。github

Orz....項目進度沒跟上,又要加班了。。。。shell

要是有一種語言,自己自帶post,get這樣的函數那該多好啊,測試程序嘩啦嘩啦就寫完了!apache

解決方案

經過Groovy構建一個腳本環境,自帶post,get這些經常使用的函數json

效果

不再用手動引用庫了,不再用手動import類了,不再用複習好長好長的文檔了,寫測試腳本再也腰不疼、腿不麻了!bash

原理

groovy自己是一個強大的腳本引擎,同時也是高度可定製化的。
在groovy編譯腳本的時候,能夠爲腳本指定一個基類,這個基類的全部方法和屬性均可以直接在腳本引用。app

實現

首先,先新建一個工程,這裏用gradle做爲構建工具,取工程名爲httpbatcheclipse

新建build.gradlemaven

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
相關文章
相關標籤/搜索