簡體   English   中英

如何在慣用的 Go 中測試預期的格式錯誤?

[英]How to test an expected formatted error in idiomatic Go?

這個問題與驗證無關。 這只是一個示例,但可能是任何其他帶有格式錯誤的東西。

假設我有一個非常簡單的Validate()方法來驗證擴展,例如。 .vcf來自文件名,如果它們不匹配,我想要一個錯誤,說明有效擴展名和當前擴展名。 理想情況下,此方法將擴展為進行其他驗證並返回其他驗證錯誤。

就像是:

// main.go
package main

import (
    "fmt"
    "os"
    "path/filepath"
)

type ValidationError struct {
    Msg string
}

func (v ValidationError) Error() string {
    return v.Msg
}

func Validate(fileName string) error {
    // first validation
    wantExtension := ".vcf"
    gotExtension := filepath.Ext(fileName)
    if gotExtension != wantExtension {
        return ValidationError{Msg: fmt.Sprintf("Extension %q not accepted, please use a %s file.", gotExtension, wantExtension)}
    }
    // potentially other validations
    return nil
}

func main() {
    fileName := os.Args[1]
    Validate(fileName)
}

我想以一種不僅檢查錯誤類型(在本例中為錯誤,還檢查消息)的方式對其進行測試。 就像是:

// main_test.go
package main

import (
    "errors"
    "testing"
)

func TestValidate(t *testing.T) {
    testCases := []struct {
        name        string
        fileName    string
        expectedErr error
    }{
        {
            name:     "happy-path",
            fileName: "file.vcf",
        },
        {
            name:        "wrong-extension",
            fileName:    "file.md",
            expectedErr: ValidationError{Msg: "Extension .md not accepted, please use a .vcf file."},
        },
    }
    for _, tc := range testCases {
        t.Run(tc.name, func(t *testing.T) {
            err := Validate(tc.fileName)
            if !errors.Is(err, tc.expectedErr) {
                t.Errorf("want (%v) got (%v)", tc.expectedErr, err)
            }
        })
    }
}

該測試將獲得:

--- FAIL: TestValidate (0.00s)
    --- FAIL: TestValidate/wrong-extension (0.00s)
        main_test.go:28: want (Extension .md not accepted, please use a .vcf file.) got (Extension ".md" not accepted, please use a .vcf file.)

由於errors.Is errors.Is()期望相同的memory object,因此我無法工作。 此外,如果我使用errors.As()測試將通過,但它不會真正檢查消息是否正確。

我還嘗試定義一個 function 以將消息作為參數或更簡單的錯誤返回錯誤errors.New()而不是自定義錯誤,但我遇到了同樣的問題,即 ZCD69B4957F06CD818D7BF3D61980E2 中的 object 中的 object 也不同知道這三種方法中的哪一種會被認為更慣用。

你將如何實現它?

更新:我更新了問題代碼以使用自定義錯誤而不是錯誤。新因為它可能更適合errors.New的答案中的情況。

我建議定義您自己的類型並讓該類型實現Error接口。 在您的測試中,您可以使用errors.As來確定返回的錯誤是新類型的實例還是包裝新類型實例的錯誤。

package main

import (
    "errors"
    "fmt"
    "path/filepath"
)

type ValidationError struct {
    WantExtension string
    GotExtension  string
}

func (v ValidationError) Error() string {
    return fmt.Sprintf("extension %q not accepted, please use a %s file.", v.GotExtension, v.WantExtension)
}

func Validate(fileName string) error {
    fileExtension := filepath.Ext(fileName)
    if fileExtension != ".vcf" {
        return ValidationError{WantExtension: ".vcf", GotExtension: fileExtension}
    }
    return nil
}

func main() {
    fileName := "something.jpg"

    err := Validate(fileName)
    var vErr ValidationError
    if errors.As(err, &vErr) {
        // you can now use vErr.WantExtension and vErr.GotExtension
        fmt.Println(vErr)
        fmt.Println(vErr.WantExtension)
        fmt.Println(vErr.GotExtension)
    }
}

哪個打印:

extension ".jpg" not accepted, please use a .vcf file.
.vcf
.jpg

編輯:感謝errors.As Farrell 關於使用錯誤的建議。作為類型斷言。 即使返回的錯誤包含ValidationError的實例,而類型斷言則不會,使用errors.As會做你想做的事。

使用errors.Is沒有問題。 該代碼在使用它時沒有失敗。

是測試代碼設置不正確。

在程序中,錯誤信息被定義為Extension ".md" not accepted, please use a.vcf file. , 和 in 一樣, return ValidationError{Msg: fmt.Sprintf("Extension %q not accepted, please use a %s file.", gotExtension, wantExtension)}

在測試中,錯誤信息定義為expectedErr: ValidationError{Msg: "Extension.md not accepted, please use a.vcf file."},

因此,當嘗試比較兩個錯誤值時,state 類似於

    fmt.Println(
        errors.Is(
            ValidationError{Msg: `Extension ".md" not accepted, please use a .vcf file.`},
            ValidationError{Msg: `Extension .md not accepted, please use a .vcf file.`},
        ),
    )

使用 api 預計會返回 false,因為它執行可比較值的相等檢查。

https://cs.opensource.google/go/go/+/refs/tags/go1.17.6:src/errors/wrap.go;l=46

對於,參考,它大致相當於

    fmt.Println(
        ValidationError{Msg: `Extension ".md" not accepted, please use a .vcf file.`} ==
            ValidationError{Msg: `Extension .md not accepted, please use a .vcf file.`},
    )

https://go.dev/ref/spec#Comparison_operators

如果結構值的所有字段都是可比較的,則結構值是可比較的。 如果它們對應的非空白字段相等,則兩個結構值相等。

最肯定使用錯誤,這個也應該提到

接口值具有可比性。 如果兩個接口值具有相同的動態類型和相同的動態值,或者兩者都具有值 nil,則它們是相等的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM