• Golang语言HTTP客户端实践


    最近在学习Golang语言,中间遇到一个前辈指点,有一个学习原则:Learning By Doing。跟我之前学习Java的经验高度契合。在前一段时间学习洼坑中挣扎了好几天,差点就忘记这个重要的成功经验。

    那么那什么来做练习呢?当然结合当下的工作啦,所以我列了一个路线给自己,那就是从接口测试开始学起来,从功能测试到性能测试,然后掌握基本Server开发技能。

    首先,得先把HTTP接口测试常用的几个功能实现了,主要是获取HTTPrequest对象,发送请求解析响应以及HttpClient的基本配置。

    这里实现比较简单和粗浅,让我想起FunTester测试框架一开始的时候,也是从封装HttpClient.jar提供的API开始的,感慨万千。

    这里我从了Golang SDK自带的net/http包提供的HTTP相关API,虽然本身提供了http.PostForm()http.Post()以及http.Get()封装好的方法,但是在处理HTTPrequest的header和cookie处理缺乏灵活性,所以我重新将net/http封装的API进行第二次封装。其中存在几处遗漏,比如HTTPstatus的判断以及header中Content-Type自动处理,这个以后再丰富,暂时的目标就是能用。

    HTTP客户端封装

    package task
    
    import (
    	"bytes"
    	"encoding/json"
    	"fmt"
    	"io/ioutil"
    	"net/http"
    	"net/url"
    	"strings"
    	"time"
    )
    
    var Client http.Client = clients()
    
    // Res 模拟响应结构
    // @Description:
    type Res struct {
    	Have string `json:"Have"`
    }
    
    // Get 获取GET请求
    // @Description:
    // @param uri
    // @param args
    // @return *http.Request
    func Get(uri string, args map[string]interface{}) *http.Request {
    	uri = uri + ToValues(args)
    	request, _ := http.NewRequest("get", uri, nil)
    	return request
    }
    
    // PostForm POST接口form表单
    // @Description:
    // @param path
    // @param args
    // @return *http.Request
    func PostForm(path string, args map[string]interface{}) *http.Request {
    	request, _ := http.NewRequest("post", path, strings.NewReader(ToValues(args)))
    	return request
    }
    
    // PostJson POST请求,JSON参数
    // @Description:
    // @param path
    // @param args
    // @return *http.Request
    func PostJson(path string, args map[string]interface{}) *http.Request {
    	marshal, _ := json.Marshal(args)
    	request, _ := http.NewRequest("post", path, bytes.NewReader(marshal))
    
    	return request
    }
    
    // ToValues 将map解析成HTTP参数,用于GET和POST form表单
    // @Description:
    // @param args
    // @return string
    func ToValues(args map[string]interface{}) string {
    	if args != nil && len(args) > 0 {
    		params := url.Values{}
    		for k, v := range args {
    			params.Set(k, fmt.Sprintf("%v", v))
    		}
    		return params.Encode()
    	}
    	return ""
    }
    
    // Response 获取响应详情,默认[]byte格式
    // @Description:
    // @param request
    // @return []byte
    func Response(request *http.Request) []byte {
    	res, err := Client.Do(request)
    	if err != nil {
    		return nil
    	}
    	body, _ := ioutil.ReadAll(res.Body) // 读取响应 body, 返回为 []byte
    	defer res.Body.Close()
    	return body
    }
    
    // clients 初始化请求客户端
    // @Description:
    // @return http.Client
    func clients() http.Client {
    	return http.Client{
    		Timeout: time.Duration(5) * time.Second, //超时时间
    		Transport: &http.Transport{
    			MaxIdleConnsPerHost:   5,   //单个路由最大空闲连接数
    			MaxConnsPerHost:       100, //单个路由最大连接数
    			IdleConnTimeout:       90 * time.Second,
    			TLSHandshakeTimeout:   10 * time.Second,
    			ExpectContinueTimeout: 1 * time.Second,
    		},
    	}
    }
    
    // ParseRes 解析响应
    // @Description:
    // @receiver r
    // @param res
    func (r *Res) ParseRes(res []byte) {
    	json.Unmarshal(res, r)
    }
    
    // ParseRes 解析响应,将[]byte转成传入对象
    // @Description:
    // @param res
    // @param r
    //
    func ParseRes(res []byte, r interface{}) {
    	json.Unmarshal(res, r)
    }
    
    

    测试脚本

    package main
    
    import (
    	"fmt"
    	"funtester/src/task"
    	"io"
    	"log"
    	"net/http"
    	"os"
    	"time"
    )
    
    const (
    	a = iota
    	b
    	c
    	d
    	e
    )
    
    func init() {
    	os.Mkdir("./log/", 0777)
    	os.Mkdir("./long/", 0777)
    	file := "./log/" + string(time.Now().Format("20060102")) + ".log"
    	openFile, _ := os.OpenFile(file, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
    	writer := io.MultiWriter(os.Stdout, openFile)
    	log.SetOutput(writer)
    	log.SetFlags(log.LstdFlags | log.Lshortfile | log.Ldate)
    }
    
    func main() {
    
    	url := "http://localhost:12345/test"
    	args := map[string]interface{}{
    		"name": "FunTester",
    		"fun":  "fdsafj",
    	}
    	cookie := &http.Cookie{
    		Name:  "token",
    		Value: "fsjej09u0934jtej",
    	}
    	get := task.Get(url, args)
    	get.Header.Add("user_agent", "FunTester")
    	get.AddCookie(cookie)
    	response := task.Response(get)
    	fmt.Println(string(response))
    	form := task.PostForm(url, args)
    	bytes := task.Response(form)
    	fmt.Println(string(bytes))
    	json := task.PostJson(url, args)
    	res := task.Response(json)
    	fmt.Println(string(res))
    
    }
    
    

    控制台输出

    GOROOT=/usr/local/go #gosetup
    GOPATH=/Users/oker/go #gosetup
    /usr/local/go/bin/go build -o /private/var/folders/7b/0djfgf7j7p9ch_hgm9wx9n6w0000gn/T/GoLand/___go_build_funtester_src_m funtester/src/m #gosetup
    /private/var/folders/7b/0djfgf7j7p9ch_hgm9wx9n6w0000gn/T/GoLand/___go_build_funtester_src_m
    get请求
    post请求form表单
    post请求json表单
    
    Process finished with the exit code 0
    
    

    测试服务

    依旧采用了moco_FunTester测试框架实现。

    package com.mocofun.moco.main
    
    import com.funtester.utils.ArgsUtil
    import com.mocofun.moco.MocoServer
    
    class Share extends MocoServer {
    
        static void main(String[] args) {
            def util = new ArgsUtil(args)
            //                def server = getServerNoLog(util.getIntOrdefault(0,12345))
            def server = getServer(util.getIntOrdefault(0, 12345))
            server.get(urlStartsWith("/test")).response("get请求")
            server.post(both(urlStartsWith("/test"), existForm("fun"))).response("post请求form表单")
            server.post(both(urlStartsWith("/test"), existParams("fun"))).response("post请求json表单")
            server.get(urlStartsWith("/qps")).response(qps(textRes("恭喜到达QPS!"), 1))
    //        server.response(delay(jsonRes(getJson("Have=Fun ~ Tester !")), 1000))
            server.response("Have Fun ~ Tester !")
            def run = run(server)
            waitForKey("fan")
            run.stop()
        }
    }
    
    
    

    Have Fun ~ Tester !

  • 相关阅读:
    PLSQL登录弹出空白框如何解决
    mongodb常用命令
    js多线程?
    Rhino -- 基于java的javascript实现
    [原创]在Docker上部署mongodb分片副本集群。
    [原创]在Linux系统Ubuntu14.04上安装部署docker。
    [原创]Win7、Win8、Win10始终以管理员身份运行程序。
    [原创]WPF应用MediaPlayer播放声音断续、不全解决方案
    [原创]C#引用C++编译的dll
    [原创]导出CSV文件,特殊字符处理。
  • 原文地址:https://www.cnblogs.com/FunTester/p/15384157.html
Copyright © 2020-2023  润新知