gin的覆盖测试无覆盖率的问题,是因为不是直接调用的函数而是通过模拟请求来触发的函数

添加自定义验证器

// 定义方法  
func CustomTimeFormat(fl validator.FieldLevel) bool {  
   _, err := time.Parse("15:04", fl.Field().String())  
   if err != nil {  
      return false  
   }  
   return true  
}

// 将自定义验证器注册到默认的验证器实例中  
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {  
   v.RegisterValidation("CustomTimeFormat", xvalidator.CustomTimeFormat)  
}

type D struct {
ServiceTime string `json:"serviceTime" binding:"required,CustomTimeFormat"`
}

常用binding

binding:"lt=0" // 小于
binding:"gt=0" // 小于
binding:"lte=0" // 小于等于
binding:"gte=0" // 大于等于
binding:"required" // 必填
binding:"omitempty,oneof=1 2" // 1或2

更多参考:https://github.com/go-playground/validator

  
type appkey struct {  
	Appkey string `json:"appkey" form:"appkey"`  
}  
  
type QueryTest struct {  
	Page int `json:"page" form:"page"`  
	Size int `json:"size" form:"size"`  
	appkey  
}  
  
type FooStruct struct {  
	Foo string `msgpack:"foo" json:"foo" form:"foo" xml:"foo" binding:"required,max=32"`  
}  
  
type FooBarStruct struct {  
	FooStruct  
	Bar string `msgpack:"bar" json:"bar" form:"bar" xml:"bar" binding:"required"`  
}  
  
type FooBarFileStruct struct {  
	FooBarStruct  
	File *multipart.FileHeader `form:"file" binding:"required"`  
}  
  
type FooBarFileFailStruct struct {  
	FooBarStruct  
	File *multipart.FileHeader `invalid_name:"file" binding:"required"`  
	// for unexport test  
	data *multipart.FileHeader `form:"data" binding:"required"`  
}  
  
type FooDefaultBarStruct struct {  
	FooStruct  
	Bar string `msgpack:"bar" json:"bar" form:"bar,default=hello" xml:"bar" binding:"required"`  
}  
  
type FooStructUseNumber struct {  
	Foo any `json:"foo" binding:"required"`  
}  
  
type FooStructDisallowUnknownFields struct {  
	Foo any `json:"foo" binding:"required"`  
}  
  
type FooBarStructForTimeType struct {  
	TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_utc:"1" time_location:"Asia/Chongqing"`  
	TimeBar time.Time `form:"time_bar" time_format:"2006-01-02" time_utc:"1"`  
	CreateTime time.Time `form:"createTime" time_format:"unixNano"`  
	UnixTime time.Time `form:"unixTime" time_format:"unix"`  
}  
  
type FooStructForTimeTypeNotUnixFormat struct {  
	CreateTime time.Time `form:"createTime" time_format:"unixNano"`  
	UnixTime time.Time `form:"unixTime" time_format:"unix"`  
}  
  
type FooStructForTimeTypeNotFormat struct {  
	TimeFoo time.Time `form:"time_foo"`  
}  
  
type FooStructForTimeTypeFailFormat struct {  
	TimeFoo time.Time `form:"time_foo" time_format:"2017-11-15"`  
}  
  
type FooStructForTimeTypeFailLocation struct {  
	TimeFoo time.Time `form:"time_foo" time_format:"2006-01-02" time_location:"/asia/chongqing"`  
}  
  
type FooStructForMapType struct {  
	MapFoo map[string]any `form:"map_foo"`  
}  
  
type FooStructForIgnoreFormTag struct {  
	Foo *string `form:"-"`  
}  
  
type InvalidNameType struct {  
	TestName string `invalid_name:"test_name"`  
}  
  
type InvalidNameMapType struct {  
	TestName struct {  
		MapFoo map[string]any `form:"map_foo"`  
	}  
}  
  
type FooStructForSliceType struct {  
	SliceFoo []int `form:"slice_foo"`  
}  
  
type FooStructForStructType struct {  
	StructFoo struct {  
		Idx int `form:"idx"`  
	}  
}  
  
type FooStructForStructPointerType struct {  
	StructPointerFoo *struct {  
		Name string `form:"name"`  
	}  
}  
  
type FooStructForSliceMapType struct {  
	// Unknown type: not support map  
	SliceMapFoo []map[string]any `form:"slice_map_foo"`  
}  
  
type FooStructForBoolType struct {  
	BoolFoo bool `form:"bool_foo"`  
}  
  
type FooStructForStringPtrType struct {  
	PtrFoo *string `form:"ptr_foo"`  
	PtrBar *string `form:"ptr_bar" binding:"required"`  
}  
  
type FooStructForMapPtrType struct {  
	PtrBar *map[string]any `form:"ptr_bar"`  
}

实现一个客户端长链接

package main  
  
import (  
	"fmt"  
	"github.com/gin-gonic/gin"  
	"time"  
)  
  
func main() {  
	r := gin.Default()  
	  
	r.GET("/stream", func(c *gin.Context) {  
		c.Header("Content-Type", "text/event-stream")  
		c.Header("Cache-Control", "no-cache")  
		c.Header("Connection", "keep-alive")  
		c.Header("Access-Control-Allow-Origin", "*")  
		  
		messageChan := make(chan string)  
		  
		go func() {  
			ticker := time.NewTicker(1 * time.Second)  
			defer ticker.Stop()  
		  
			for {  
				select {  
					case <-ticker.C:  
					messageChan <- "This is a message"  
				}  
			}  
		}()  
		  
		for {  
			message := <-messageChan  
			fmt.Fprintf(c.Writer, "data: %s\n\n", message)  
			c.Writer.Flush()  
		}  
	})  
	  
	r.Run(":8080")  
}