Ответ 1
Здесь есть несколько вещей:
Ошибки
Значения экспортированных ошибок уровня пакета обычно называются Err
, за которым следует что-то, например ErrTimeout
здесь. Это делается для того, чтобы клиенты вашего пакета могли делать что-то вроде
if err := yourpkg.Function(); err == yourpkg.ErrTimeout {
// timeout
} else if err != nil {
// some other error
}
Чтобы облегчить это, они часто создаются либо с помощью errors.New
:
// Error constants
var (
ErrTimeout = errors.New("yourpkg: connect timeout")
ErrInvalid = errors.New("yourpkg: invalid configuration")
)
или с пользовательским, невыполненным типом:
type yourpkgError int
// Error constants
var (
ErrTimeout yourpkgError = iota
ErrSyntax
ErrConfig
ErrInvalid
)
var errText = map[yourpkgError]string{
ErrTimeout: "yourpkg: connect timed out",
...
}
func (e yourpkgError) Error() string { return errText[e] }
Одним из преимуществ последнего подхода является то, что он не может сравниться с типом любого другого пакета.
В случае, если вам нужны дополнительные данные внутри ошибки, имя типа заканчивается на Error
:
type SyntaxError struct {
File string
Line, Position int
Description string
}
func (e *SyntaxError) Error() string {
return fmt.Sprintf("%s:%d:%d: %s", e.File, e.Line, e.Position, e.Description)
}
который, в отличие от предыдущей проверки равенства, требует утверждения типа:
tree, err := yourpkg.Parse(file)
if serr, ok := err.(*SyntaxError); ok {
// syntax error
} else if err != nil {
// other error
}
В любом случае важно документировать свой код, чтобы пользователи вашего пакета понимали, когда они будут использоваться, и какие функции могут их вернуть.
Испытания
Тесты часто называются после единицы, которую они тестируют. Во многих случаях вы не будете тестировать условия ошибки отдельно, поэтому TestError
не является именем, которое должно появляться очень часто. Название самого теста просто должно быть уникальным, однако оно не ограничено ничем в тестируемом коде тем же способом, что и в примерах. Когда вы тестируете несколько условий кода, часто лучше всего сформулировать тест как Table Driven Test. На этой странице wiki есть несколько хороших примеров, но чтобы продемонстрировать проверку ошибок, вы можете сделать это:
func TestParse(t *testing.T) {
tests := []struct{
contents string
err error
}{
{"1st", nil},
{"2nd", nil},
{"third", nil},
{"blah", ErrBadOrdinal},
{"", ErrUnexpectedEOF},
}
for _, test := range tests {
file := strings.NewReader(test.contents)
if err := Parse(file); err != test.err {
t.Errorf("Parse(%q) error %q, want error %q", test.contents, err, test.err)
}
// other stuff
}
}
Если вам нужна специальная тестовая функция для устройства, которое делает что-то странное и не соответствует основному тесту, вы обычно называете его чем-то описательным, например TestParseTimeout
, который включает как устройство, так и поведение, повторное тестирование.