Go语言入坑记录

原因

本来主要使用Python,但是由于运行环境受限制的原因,依赖包的维护比较麻烦。因此相中Go语言编译成单一可执行程序的好处。

目标

将现在用Python实现的一些功能,逐步替换为用Go来实现,编译成小工具程序,比如,RestFul API接口测试工具。
经过一些最简单的Hello World式的编程体验后,选择Visual Studio Code作为编辑工具,Go版本1.14,采用go mod 建项目(主要原因是要使用goproxy https://goproxy.io来下载依赖)。
确定基本目标,实现一个基本的RestFul API接口测试工具。该工具使用excel文件定义基本配置和测试用例,输出junit格式的报告和excel格式的报告,输出log文件,接受命令行参数(通过命令行参数指定测试用例文件名、日志文件名、日志记录级别等)。因此除了掌握Go语言基本编程以外,主要任务是要找到常用功能包。

语言特性相关

  1. struct ,Go没有类,struct相当于类。struct的方法也是一个函数,但是需要在func的关键字后增加一个参数定义,这个参数称为“接收者”,有点类似于Python的self。但是这个接收者有两种可选形式,一个是传值,一个是传引用,如果需要改变调用者本身,则需要传引用。
  2. map ,Go没有象Python那么灵活的字典类型,对应的是map,而且需要事先确定类型。当类型不能确定,或者是任意类型的时候,就需要使用到interface ,在处理json文件的时候会遇到。
  3. 异常处理 ,Go语言中没有try那一套。它用error来处理可预见的错误,用panic来处理不可预见的异常(panic暂时还没用到)。

Excel文件读写

经过一番选择,采用了github.com/360EntSecGroup-Skylar/excelize模块,它的官方文档的位置为https://xuri.me/excelize/zh-hans/。了解OpenFile、GetSheetMap、Rows、Columns、NewSheet、SetColWidth、SetRowHeight、NewStyle、SetCellStyle、SetCellStyle、SetCellValue、SaveAs等方法,然后看看文档中的例子,就可以完成Excel文件读写。在使用与Cell相关的操作时,它采用的是excel的引用方式,如:“A2”,我们用二维数组[i][j]进行索引,需要自己转换一下。

文件操作

用go标准库中os模块的 os.Stat()、 os.IsExist()、 os.Remove()等方法完成相关操作。

json文件读写

常用的有json与map的互转,json与struct的互转。直接用encoding/json就可以了。主要的两个方法,一是json字符串转map:json.Unmarshal(),二是map字符串转json: json.Marshal。

xml文件读写

选择了一个第三方模块github.com/tinyhubs/tinydom,这个模块很小,用起来还是比较方便的。了解了NewDocument()、NewProcInst()、NewElement()、InsertEndChild()、SetAttribute()、SaveDocumentToFile()就可以产生和保存一个xml文件了。详细见https://github.com/tinyhubs/tinydom,这个模块只有一个源文件,不清楚的地方可以通过阅读源码了解。

日志文件读写

选择了一个第三方模块go.uber.org/zap,这个模块在网上有很多教程,基本可以直接借鉴。本项目稍作封装了一下,在log.go中定义全局变量logger和相关方法,在main.go中进行初始化和关闭,在其它模块中读可以直接使用。

package tools
import (
	"fmt"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"time"
)

var logger *zap.Logger

func formatEncodeTime(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(fmt.Sprintf("%d%02d%02d_%02d%02d%02d", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()))
}

func FormateLog(args []interface{}) *zap.Logger {
	log := logger.With(ToJsonData(args))
	return log
}

func Debug(msg string, args ...interface{}) {
	FormateLog(args).Sugar().Debugf(msg)
}

func Info(msg string, args ...interface{}) {
	FormateLog(args).Sugar().Infof(msg)
}

func Warn(msg string, args ...interface{}) {
	FormateLog(args).Sugar().Warnf(msg)
}

func Error(msg string, args ...interface{}) {
	FormateLog(args).Sugar().Errorf(msg)
}

func ToJsonData(args []interface{}) zap.Field {
	det := make([]string, 0)
	if len(args) > 0 {
		for _, v := range args {
			det = append(det, fmt.Sprintf("%+v", v))
		}
	}
	zap := zap.Any("detail", det)
	return zap
}

func InitZapLog(level string, logfile string) {
	
	var L zapcore.Level 

	switch level {
		case "error":
			L = zapcore.ErrorLevel
		case "info":
			L = zapcore.InfoLevel
		case "warn":
			L = zapcore.WarnLevel
		default:
			L =  zap.DebugLevel
	}

	cfg := zap.Config{
		Level:       zap.NewAtomicLevelAt(L),
		Development: true,
		Encoding:    "json",
		EncoderConfig: zapcore.EncoderConfig{
			TimeKey:        "t",
			LevelKey:       "level",
			NameKey:        "logger",
			CallerKey:      "caller",
			MessageKey:     "msg",
			StacktraceKey:  "trace",
			LineEnding:     zapcore.DefaultLineEnding,
			EncodeLevel:    zapcore.LowercaseLevelEncoder,
			EncodeTime:     formatEncodeTime,
			EncodeDuration: zapcore.SecondsDurationEncoder,
			EncodeCaller:   zapcore.ShortCallerEncoder,
		},
		OutputPaths:      []string{logfile },
		ErrorOutputPaths: []string{logfile },
		InitialFields: map[string]interface{}{
			"app": "test",
		},
	}
	var err error
	logger, err = cfg.Build()
	if err != nil {
		panic("log init fail:" + err.Error())
	}
}

func GetLogger() *zap.Logger{
	return logger
}

http client

使用标准库的net/http实现RestFul API的调用,代码片段如下。

func (e *ApiTest) RestApiCall(method string, url string, header map[string]string, body string) (string, string) {
	var failed string = "{\"msg\": \"failed\",  \"msg_code\": \"0\"}"
	var score string = "Pass"

	Info("RestApiCall Begin", method +" " + url)

	req, err := http.NewRequest(method, url, strings.NewReader(body))
	if err != nil {
		Error("request", err)
		return failed, score
	}
	for k, v := range header {
		req.Header.Set(k, v)
	}
	clt := http.Client{}
	resp, err := clt.Do(req)
	if err != nil {
		Error("request", err)
		return failed, score
	}

	defer resp.Body.Close()
	result, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		Error("reponse", err)
		return failed, score
	}
	
	Info("RestApiCall End", method +" " + url)
	return string(result), score
}

命令行参数

使用flag模块,代码片段如下。

	var setfile= flag.String("set", "setting.xlsx", "配置文件")
	var tcfile = flag.String("tc", "testcase.xlsx", "测试用例")
	var junit = flag.String("junit", "junit.xml", "junit报告")
	var xlsx = flag.String("xlsx", "report.xlsx", "Excel报告")
	var logf = flag.String("logf", "iftest.log", "log file")
	var logl = flag.String("logl", "debug", "log level")

	flag.Parse()

小结

虽然过程中遇到了不少问题,事后小结,只要熟悉了Go语言特性,找到了合适的功能模块,目标实际一点,还是能实现的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值