6

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?

3 Answers 3

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

This is indeed very clear as to what errors might be expected to occur. I'd certainly prefer to handle the function that way if calling it for real. I'm still of two minds as to whether the clarity justifies the verbosity, though.
4

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).

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.