I'm writing a JSON validator in Go, and I want to test another object that interacts with my Validator. I've implemented the Validator as a struct with methods. To allow me to inject a mock Validator into another object, I've added an interface, which the Validator implements. I've then swapped argument types to expect the interface.
// Validator validates JSON documents.
type Validator interface {
// Validate validates a decoded JSON document.
Validate(doc interface{}) (valid bool, err error)
// ValidateString validates a JSON string.
ValidateString(doc string) (valid bool, err error)
}
// SchemaValidator is a JSON validator fixed with a given schema.
// This effectively allows us to partially apply the gojsonschema.Validate()
// function with the schema.
type SchemaValidator struct {
// This loader defines the schema to be used.
schemaLoader gojsonschema.JSONLoader
validationError error
}
// Validate validates the given document against the schema.
func (val *SchemaValidator) Validate(doc interface{}) (valid bool, err error) {
documentLoader := gojsonschema.NewGoLoader(doc)
return val.validate(documentLoader)
}
// ValidateString validates the given string document against the schema.
func (val *SchemaValidator) ValidateString(doc string) (valid bool, err error) {
documentLoader := gojsonschema.NewStringLoader(doc)
return val.validate(documentLoader)
}
One of my mocks looks like this:
// PassingValidator passes for everything.
type PassingValidator bool
// Validate passes. Always
func (val *PassingValidator) Validate(doc interface{}) (valid bool, err error) {
return true, nil
}
// ValidateString passes. Always
func (val *PassingValidator) ValidateString(doc string) (valid bool, err error) {
return true, nil
}
This works, but it doesn't feel quite right. Collaborators won't see anything other than my concrete type in production code; I've only introduced the interface to suit the test. If I do this everywhere, I feel like I'll be repeating myself by writing interfaces for methods that will only ever have one real implementation.
Is there a better way to do this?