Golang校验表格测试仅在有效结构体上失败

Golang校验表格测试仅在有效结构体上失败 // 这是在验证测试中

type Report struct {
    SysrptID    json.Number `json:"SysrptID" validate:"required"`
    Name        string      `json:"Name" validate:"required"`
    Description string      `json:"Description"`
    FileName    string      `json:"FileName"`
    RptType     string      `json:"RptType" validate:"oneof=AIS MDR CTE MCM GEN RAL DOC"`
    Category    []string   
}

// 这是我的验证代码:

import (
	"fmt"
	"github.com/go-playground/validator"
	"regexp"
)

// ValidationError 包装了验证器的 FieldError,这样我们就不会将其暴露给外部代码
type ValidationError struct {
	validator.FieldError
}

func (v ValidationError) Error() string {
	return fmt.Sprintf(
		"Key: '%s' Error: Field validation for '%s' failed on the '%s' tag",
		v.Namespace(),
		v.Field(),
		v.Tag(),
	)
}

// ValidationErrors 是 ValidationError 的集合
type ValidationErrors []ValidationError

// Errors 将切片转换为字符串切片
func (v ValidationErrors) Errors() []string {
	errs := []string{}
	for _, err := range v {
		errs = append(errs, err.Error())
	}

	return errs
}

// Validation 包含
type Validation struct {
	validate *validator.Validate
}

// NewValidation 创建一个新的 Validation 类型
func NewValidation() *Validation {
	validate := validator.New()
	//validate.RegisterValidation("sku", validateSKU)

	return &Validation{validate}
}

// Validate 验证项目
// 有关更多详细信息,返回的错误可以转换为 validator.ValidationErrors 集合
//
// if ve, ok := err.(validator.ValidationErrors); ok {
//			fmt.Println(ve.Namespace())
//			fmt.Println(ve.Field())
//			fmt.Println(ve.StructNamespace())
//			fmt.Println(ve.StructField())
//			fmt.Println(ve.Tag())
//			fmt.Println(ve.ActualTag())
//			fmt.Println(ve.Kind())
//			fmt.Println(ve.Type())
//			fmt.Println(ve.Value())
//			fmt.Println(ve.Param())
//			fmt.Println()
//	}
func (v *Validation) Validate(i interface{}) ValidationErrors {
	errs := v.validate.Struct(i).(validator.ValidationErrors)

// 我认为问题出在这里 :frowning: 
	if len(errs) == 0 {
		return nil
	}

	var returnErrs []ValidationError
	for _, err := range errs {
		// 将 FieldError 转换为我们定义的 ValidationError 并追加到切片中
		ve := ValidationError{err.(validator.FieldError)}
		returnErrs = append(returnErrs, ve)
	}

	return returnErrs
}

// validateSKU 未使用,仅作为示例
func validateSKU(fl validator.FieldLevel) bool {
	// SKU 必须为 abc-abc-abc 格式
	re := regexp.MustCompile(`[a-z]+-[a-z]+-[a-z]+`)
	sku := re.FindAllString(fl.Field().String(), -1)

	if len(sku) == 1 {
		return true
	}

	return false
}

// 验证器结束

// 当我运行这个测试时,对于一个[我认为]应该是有效的报告,程序发生了恐慌 // 它从未进入 if 语句,不确定我做错了什么 // 那些验证失败的结构体工作得很好。 // 恐慌信息是: // 2021/02/17 08:13:36 panic occurred: interface conversion: error is // nil, not validator.ValidationErrors

func TestReportsValidator(t *testing.T) {
    tests := map[string]struct {
        input Report
        want  int
    }{
        //"just id":   {input: Report{SysrptID: "1"}, want: 2},
        //"just name": {input: Report{Name: "test report"}, want: 2},
        //"none": {input: Report{}, want: 3},
        "valid":   {input: Report{SysrptID: "1", Name: "test", RptType: "RAL"}, want: 0},
       //"trailing sep": {input: Report{}, want: 1},
    }

    for name, tc := range tests {
        fmt.Println(name, tc.input, tc.want)

        t.Run(name, func(t *testing.T) {
            v := NewValidation()
            defer func() {
                if err := recover(); err != nil {
                    log.Println("panic occurred:", err)
                }
            }()

            fe := v.Validate(tc.input)
            if fe != nil {
                assert.Len(t, fe, tc.want)
            }

            //diff := cmp.Diff(tc.want, got)
            //if diff != "" {
            //  t.Fatalf(diff)
            //}
        })

    }

}

更多关于Golang校验表格测试仅在有效结构体上失败的实战教程也可以访问 https://www.itying.com/category-94-b0.html

4 回复

谢谢!!

更多关于Golang校验表格测试仅在有效结构体上失败的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


请将你的代码用三个反引号包裹起来,就像这样:

code here

Go 语言中的逗号 ok 模式派上了用场,这意味着如果没有验证问题,我可以返回 nil。某个 YouTube 代码中存在一个严重的错误。通过一个测试修复了它。这个问题发生在我服务中添加一个本应通过验证的项目时。太酷了。

func (v *Validation) Validate(i interface{}) ValidationErrors {
	var returnErrs []ValidationError

	if errs, ok := v.validate.Struct(i).(validator.ValidationErrors); ok {

		if errs != nil {
			for _, err := range errs {
				if fe, ok := err.(validator.FieldError); ok {
					ve := ValidationError{fe}
					returnErrs = append(returnErrs, ve)
				}
			}
		}
	}

	return returnErrs
}

问题出现在 Validate 方法中的类型断言。当验证通过时,v.validate.Struct(i) 返回 nil,但代码试图将其断言为 validator.ValidationErrors,这会导致 panic。

以下是修复后的 Validate 方法:

func (v *Validation) Validate(i interface{}) ValidationErrors {
    err := v.validate.Struct(i)
    if err == nil {
        return nil
    }

    // 只有当 err 不是 nil 时才进行类型断言
    errs, ok := err.(validator.ValidationErrors)
    if !ok {
        // 处理非验证错误的情况
        return ValidationErrors{
            ValidationError{fieldError: err},
        }
    }

    var returnErrs []ValidationError
    for _, err := range errs {
        ve := ValidationError{err}
        returnErrs = append(returnErrs, ve)
    }

    return returnErrs
}

还需要更新 ValidationError 结构体以处理非 FieldError 的情况:

type ValidationError struct {
    fieldError error
}

func (v ValidationError) Error() string {
    if fe, ok := v.fieldError.(validator.FieldError); ok {
        return fmt.Sprintf(
            "Key: '%s' Error: Field validation for '%s' failed on the '%s' tag",
            fe.Namespace(),
            fe.Field(),
            fe.Tag(),
        )
    }
    return v.fieldError.Error()
}

这样修改后,当验证通过时不会发生 panic,测试用例也能正确处理有效结构体。

回到顶部