问题
I'm trying to compose a simple API client I'm stuck trying to figure out how to make it readable and testable. How can I compose a nested structure while keeping it testable?
Psuedo code:
type VehicleEndpoint struct {
Car CarEndpoint
VehicleGetter
}
type VehicleGetter interface {
Get(string) Vehicle
}
type Vehicle struct {
kind string
}
type VehicleClient struct {
http.Client
url string
}
func (v *VehicleClient) Get(kind string) Vehicle {
resp := v.Do(v.url, kind)
return Vehicle{
kind: resp.Kind
}
}
type CarEndpoint struct
...
type CarGetter interface
...
type Car struct
...
type CarClient struct
...
type API struct {
Vehicle VehicleEndpoint
}
api := API{
Vehicle: VehicleEndpoint{
VehicleGetter: VehicleClient{
http.Client{},
}
Car: CarEndpoint{
CarGetter: CarClient{
http.Client{},
}
}
}
}
Now I can call API like so:
api.Vehicle.Car.Get(kind)
This gives me a very readable (nested) implementation to work with however I'm having a hard time mocking these endpoints because the use of interface would effectively remove any recognition of the nested structure. What would be the recommended way to construct an API keeping it very readable while also having each endpoint mocked?
回答1:
You are fighting with language and bringing your OOP hobbies into language that is not designed for that.
I personally would change direction and use old good flat structures and functions.
Although, if you want to continue with your design, you can mock not interface but whole http
stack. You can test your code with much better confidence as you testing real http
payload vs making calls to your interfaces.
Inject HttpClient
into Vehicle
: func NewVehicle(httpClient *http.Client){}
In test code, use *http.ServeMux
:
mux.Handle("/path1", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// assessments and mocked response
}))
mux.Handle("/path2", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// assessments and mocked response
}))
// fallback to show not implemented routes
result.mux.Handle("/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
result.t.Errorf("Not Supported route %q", r.URL.Path)
}))
Build Http Server:
server := httptest.NewServer(mux)
Create Http Client from mux server:
client := server.Client()
来源:https://stackoverflow.com/questions/53231070/how-to-nest-endpoints-in-a-api-client-with-interfaces-while-keeping-readability