How to nest endpoints in a API client with interfaces while keeping readability

我怕爱的太早我们不能终老 提交于 2019-12-13 02:54:41

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!