本来主要使用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语言基本编程以外,主要任务是要找到常用功能包。
经过一番选择,采用了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与map的互转,json与struct的互转。直接用encoding/json就可以了。主要的两个方法,一是json字符串转map:json.Unmarshal(),二是map字符串转json: json.Marshal。
选择了一个第三方模块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
}
使用标准库的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语言特性,找到了合适的功能模块,目标实际一点,还是能实现的。