Let's say I have an app with three layers: a view (JSON endpoint), service, and persistence layer.
Now a `NotFoundError` error occurs at the persistence layer when a record cannot be found. `NotFoundError` is a simple wrapper around a lower level database driver error that I don't want the application to be aware of (since it's an implementation detail). For example, if the `gocql` driver emits an `ErrNotFound` error, the persistence layer will wrap it in a custom error type so that the original error is preserved but not exposed. package database type NotFoundError struct { originalErr error // persistence layer wraps the driver error } The service layer then type switches on whatever errors it receives from lower layers. For example in a call to the persistence layer: package service func (s *Service) GetSomething(id string) (string, error) { s, err := database.Get(id) if err != nil { return "", handleError(err, “service: failed to get something”) } } func handleError(err error, context, message string) error { switch err.(type) { case database.NotFoundError: return &ServiceError{ Code: NotFoundCode, Message: message, originalErr: errors.Wrap(err, context), } default: ... } } a service error is a custom error type that looks like: type ServiceError struct { originalErr error Code int `json:"code"` Field string `json:"target,omitempty"` Message string `json:"message,omitempty"` Details []*ServiceError `json:"details,omitempty"` } To recap, the error propagates through the following layers: driver -> persistence -> service -> view Each layer type switches on the error type it receives from the preceding layer. Errors may be recursively nested (the "Details" field is a []*ServiceError) and "originalErr" contains the original error with a stack trace provided by the pkg/errors <https://github.com/pkg/errors> library. How would one approach unit testing something like this? Doing a simple reflect.DeepEqual on the actual and expected error values would be ideal, but the stack trace contained in the actual error means that the expected error will always fail the equality comparison (since DeepEqual also parses unexported fields). In addition, the order of errors in the `Details` slice is also unreliable (for example when validating fields, the order shouldn't matter) which further complicates trying to compare things. I’d have to pollute my tests with loops, manual comparisons and recursive logic which will itself be error-prone. I’ve looked through some popular projects on GitHub and I can’t find any examples of code similar to this this which leads me to believe that all these abstraction hierarchies and complex errors types are horribly unidiomatic Go... I now know why people say Go is more of a systems language. But anyway... is there a better way to deal with errors in an app structured like this? How would you go about comparing these types of error values? How do you handle rich errors types with lots of contextual information in your own web applications? -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.