Greetings
I am writing a piece of code that rely on an api with code like this
package awesome
struct HttpError {
code int
}
func (e *HttpError) GetCode() int {
return e.code
}
struct Client {}
func (c *Client) AwesomeOp() error {
// maybe 404
return &HttpError{code:404}
// or maybe other things will happen
}
My code uses AwesomeOp()
and perform some operation when I get a 404 error like this
type myAwesomeInterface interface {
AwesomeOp() error
}
type Bar struct {
// I can totally use awesome.Client here in production
// and mock my own object with the same interface
client myAwesomeInterface
}
func (b *Bar) Foo() {
err := b.client.AwesomeOp()
if httpErr, isHttpErr := err.(awesome.HttpError); isHttpErr {
errorCode := httpErr.GetCode()
if errorCode == 404 {
// THIS is untestable since I can't create my own HttpError with error == 404
}
}
}
I found that the 404 code path is impossible to test because my mock object cannot create a 404 HttpError
since the httpError.code
field is private. I considered doing this:
type myHttpError interface {
GetCode() int
}
func (b *Bar) TestableButBrokenFoo() {
err := b.client.AwesomeOp()
if httpErr, isHttpErr := err.(myHttpError); isHttpErr {
errorCode := httpErr.GetCode()
if errorCode == 404 {
// untestable since I can't create my own
}
}
}
This implementation is now functional and testable but it is incorrect. If the team for awesome
package change the signature of GetCode()
to something else. My code would still build, the unit tests would even pass without giving me an issue.
Is there a way to claim awesome.HttpError
must implement myHttpError
interface so I can discover that they change the method and break my code at build time? That’s ideally what I want.