在Go语言中进行单元表测试的错误类型检查

huangapple go评论82阅读模式
英文:

Error type checking in Go for unit table tests

问题

我想要测试返回的错误类型与预期结果的测试表的类型,像这样:

var tabletest = []struct{
  instruction string
  want string
  err error
}{
  {"synonym for hi", "hello", nil}, // 输入,返回值,错误类型
  {"synonym for hig", "", TranslationError{}}, 
  {"sssnymm for hi", "", InstructionError{}},
}

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)
    // 检查错误类型
    // 检查结果类型
  }
}

在上面的示例中,根据发生的错误类型返回不同的错误子类。你可以想象,虚构的 Thesaurus 函数的调用者会根据每种错误类型进行不同的处理。

如何以惯用方式断言返回的错误类型和预期的错误类型相同?

英文:

I want to test the type of the error returned against a table test of expected results, like so:

var tabletest = []struct{
  instruction string
  want string
  err error
}{
  {"synonym for hi", "hello", nil}, // input, retval, errtype
  {"synonym for hig", "", TranslationError{}}, 
  {"sssnymm for hi", "", InstructionError{}},
}

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)
    // check error type
    // check result type
  }
}

In the example above, different error sub-classes are returned based on the type of error that occurred. You may imagine that the caller of the made-up Thesaurus function would handle each error type differently.

What is the idiomatic way to assert that the type of error returned, and the type of error expected, are the same?

答案1

得分: 5

使用type开关。

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)

    // 不使用&&,因为我们想捕获所有err为nil的情况
    if err == nil  {
      if testcase.err != nil {
          // 失败
      }
      continue
    }

    switch err.(type) {
    case TranslationError:
        if _,ok := (testcase.err).(TranslationError); !ok {
           // 失败
        }
    case InstructionError:
        if _,ok := (testcase.err).(InstructionError); !ok {
           // 失败
        }
    default:
        // 无法识别的错误,失败
    }
  }
}

这绝对不如使用reflect的方式简洁,但我认为它更符合Go语言的风格,更加明确。

英文:

Use a type switch.

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)

    // Don't use && because we want to trap all cases where err is nil
    if err == nil  {
      if testcase.err != nil {
          // failure
      }
      continue
    }

    switch err.(type) {
    case TranslationError:
        if _,ok := (testcase.err).(TranslationError); !ok {
           // failure
        }
    case InstructionError:
        if _,ok := (testcase.err).(InstructionError); !ok {
           // failure
        }
    default:
        // Unrecognized error, failure
    }
}

It's definitely not as succinct as the reflect way of doing it, but I think it's more Go-ish and explicit.

答案2

得分: 4

这里还有一个成语:

在同义词库中...

import "errors"

var (
    TranslationError = errors.New("")
    InstructionError = errors.New("")
)

在测试用例中...

if err != testcase.err {

}

然而,我认为在这个成语中,错误必须事先定义好(即消息不能更改)。

英文:

There's also this idiom:

In Thesaurus...

import "errors"

var (
	TranslationError = errors.New("")
	InstructionError = errors.New("")
)

In Testcase...

if err != testcase.err {

}

However, I think in this idiom the errors must be defined in advance (i.e. the message cannot be changed).

答案3

得分: 2

reflect.TypeOf可以完成这个任务:

import "reflect"

...

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)
    // 检查错误类型
    if goterr, wanterr := reflect.TypeOf(err), reflect.TypeOf(testcase.err); 
         goterr != wanterr {
      t.Errorf("对于指令 %q,出现了意外的错误:%q。期望的是 %q", 
               testcase.instruction, goterr, wanterr)
    }
    // 检查结果类型
    if want := testcase.want; got != want {
      t.Errorf("对于指令 %q,得到了 %q,期望的是 %q", 
               testcase.instruction, got, want)
    }
  }
}

是否符合惯用用法由社区决定。

英文:

reflect.TypeOf does the job:

import "reflect"

...

func TestThesaurus(t *Testing) {
  for _, testcase := range tabletest {
    got, err := Thesaurus(testcase.instruction)
    // check error type
    if goterr, wanterr := reflect.TypeOf(err), reflect.TypeOf(testcase.err); 
         goterr != wanterr {
      t.Errorf("For instruction %q, unexpected error: %q. Wanted %q", 
               testcase.instruction, goterr, wanterr)
    }
    // check result type
    if want := testcase.want; got != want {
      t.Errorf("For instruction %q, got %q, want %q.", 
               testcase.instruction, got, want)
    }
  }
}

Whether or not it's idiomatic is for the community to decide.

huangapple
  • 本文由 发表于 2014年4月30日 03:30:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/23373633.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定