[英]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.