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

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?

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()

Related

How do I improve the testability of go library methods

I'm writing some code that uses a library called Vault. In this library we have a Client. My code makes use of this Client but I want to be able to easily test the code that uses it. I use only a couple methods from the library so I ended up creating an interface:
type VaultClient interface {
Logical() *api.Logical
SetToken(v string)
NewLifetimeWatcher(i *api.LifetimeWatcherInput) (*api.LifetimeWatcher, error)
}
Now if my code is pointed at this interface everything is easily testable.. Except let's look at the Logical() method. It returns a struct here. My issue is that this Logical struct also has methods on it that allow you to Read, Write, ex:
func (c *Logical) Read(path string) (*Secret, error) {
return c.ReadWithData(path, nil)
}
and these are being used in my project as well to do something like:
{{ VaultClient defined above }}.Logical().Write("something", something)
Here is the issue. The Logical returned from the call to .Logical() has a .Write and .Read method that I can't reach to mock. I don't want all the logic within those methods to run in my tests.
Ideally I'd like to be able to do something similar to what I did above and create an interface for Logical as well. I'm relatively new to Golang, but I'm struggling with the best approach here. From what I can tell that's not possible. Embedding doesn't work like inheritance so it seems like I have to return a Logical. That leaves my code unable to be tested as simply as I would like because all the logic within a Logical's methods can't be mocked.
I'm sort of at a loss here. I have scoured Google for an answer to this but nobody ever talks about this scenario. They only go as far as I went with the initial interface for the client.
Is this a common scenario? Other libraries I've used don't return structs like Logical. Instead they typically just return a bland struct that holds data and has no methods.
package usecasevaultclient
// usecase.go
type VaultClient interface {
Logical() *api.Logical
SetToken(v string)
NewLifetimeWatcher(i *api.LifetimeWatcherInput) (*api.LifetimeWatcher, error)
}
type vaultClient struct {
repo RepoVaultClient
}
// create new injection
func NewVaultClient(repo RepoVaultClient) VaultClient {
return &vaultClient{repo}
}
func(u *vaultClient) Logical() *api.Logical {
// do your logic and call the repo of
u.repo.ReadData()
u.repo.WriteData()
}
func(u *vaultClient) SetToken(v string) {}
func(u *vaultClient) NewLifetimeWatcher(i *api.LifetimeWatcherInput) (*api.LifetimeWatcher, error)
// interfaces.go
type RepoVaultClient interface {
ReadData() error
WriteData() error
}
// repo_vaultclient_mock.go
import "github.com/stretchr/testify/mock"
type MockRepoVaultClient struct {
mock.Mock
}
func (m *MockRepoVaultClient) ReadData() error {
args := m.Called()
return args.Error(0)
}
func (m *MockRepoVaultClient) WriteData() error {
args := m.Called()
return args.Error(0)
}
// vaultClient_test.go
func TestLogicalShouldBeSuccess(t *testing.T) {
mockRepoVaultClient = &MockRepoVaultClient{}
useCase := NewVaultClient(mockRepoVaultClient)
mockRepoVaultClient.On("ReadData").Return(nil)
mockRepoVaultClient.On("WriteData").Return(nil)
// your logics gonna make this response as actual what u implemented
response := useCase.Logical()
assert.Equal(t, expected, response)
}
if you want to test the interface of Logical you need to mock the ReadData and WriteData , with testify/mock so u can defined the respond of return of those methods and you can compare it after you called the new injection of your interface

What is the best way to have dependency injection in Golang

I'm a beginner in Golang and I'm working on a small library which need to get a DB connection at some point in the code for différent sub package / method call. I'm just wondering how I can manage this ?
Example, If I manage to have a webserver, it works with handler, so how can I get this connection inside this function ?
It could be used with another process, simple method call or MVC model ?
I don't want to use global because for me it's a bad practice except if it's very exceptional way (or tricky somehow).
I read a lot of write in different website, but still, I'm asking and learning from different opinion and experiences.
Thanks for your time !
Create a struct that represent the resource​, let's call Cart. Add get and post methods to this struct. These methods should be http handlers. In main create an instance of the struct with db interface. And in the route call Cart.get. Now in get method you have access to the db interface.
Not a working example, just to get the idea of injecting for testing.
type storage interface {
PrepareContext(context.Context, string) (*sql.Stmt, error)
}
func main() {
db, _ := sql.Open("mysql", `queryString`)
http.HandleFunc("/", Cart{db}.get)
http.ListenAndServe(":8080", nil)
}
type Cart struct {
storage
}
func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
q, _ := crt.PrepareContext(context.Background(), `select *`)
fmt.Println(q.Exec())
}
/////////Test
type testDB struct{}
func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
return nil, nil
}
func TestGet(t *testing.T) {
db := testDB{}
_ = Cart{db}
//http test here
}
I would suggest Dargo which is an injection engine in the style of Java CDI and/or JSR-330. It uses struct annotations and Injection is performed using reflection or using creator functions. It supports different lifecycles for services including Singleton (created exactly once, lazily), PerLookup (created every time injected or looked up), Immediate (created exactly once, eagerly) and DargoContext (tied to the lifecycle of a context.Context)
You can also try Hiboot which is a web/cli application framework that support dependency injection out of box.
Docs
// HelloService is a simple service interface, with interface, we can mock a fake service in unit test
type HelloService interface {
SayHello(name string) string
}
type helloServiceImpl struct {
}
func init() {
// Register Rest Controller through constructor newHelloController
// Register Service through constructor newHelloService
app.Register(newHelloController, newHelloService)
}
// please note that the return type name of the constructor HelloService,
// hiboot will instantiate a instance named helloService for dependency injection
func newHelloService() HelloService {
return &helloServiceImpl{}
}
// SayHello is a service method implementation
func (s *helloServiceImpl) SayHello(name string) string {
return "Hello" + name
}
// PATH: /login
type helloController struct {
web.Controller
helloService HelloService
}
// newHelloController inject helloService through the argument helloService HelloService on constructor
func newHelloController(helloService HelloService) *helloController {
return &helloController{
helloService: helloService,
}
}
// Get /
// The first word of method name is the http method GET
func (c *helloController) Get(name string) string {
return c.helloService.SayHello(name)
}
I would suggest giving a try to https://github.com/facebookgo/inject. It allows to define an object graph and specify dependencies with struct annotations. Injection is performed using reflection.
For an IoC container, you can try this package:
https://github.com/golobby/container
Example of singleton binding:
container.Singleton(func() Database {
return &MySQL{}
})
Example of resolving:
var db Database
container.Make(&db)
As you can see it's so easy to work with.

Change *http.Client transport

The Status Quo
Having picked a side project (building a wrapper around a third party API), I'm stuck. I am using sling to compose my HTTP requests.
So parts of the Client are composed as follows:
type Client struct {
// some services etc..
sling *sling.Sling <-- this is initialized with *http.Client
}
func NewClient(httpClient *http.Client) *Client {
sling := sling.New().Client(httpClient).Base(BaseURL)
}
//....
Things I can't wrap my head around
I am following the same principle as go-github and go-twitter that authentication should not be handled by my library, but rather by golangs oauth1/2 package.
As the the API provides application and user level authentication and some of the workflows require initial application level authentication and then user level authentication, my question is, if there is any way to change the *http.Transport in order to change the authentication header on a client basis.
So far, I haven't found a way to do so.
The http.Client has a Transport field that you could use to "change the authentication header on a client basis" if that's what you want. The Transport field has type http.RoundTripper which is a one method interface, so all you need to do is to define your transport with an implementation of the RoundTrip method.
type MyTransport struct {
apiKey string
// keep a reference to the client's original transport
rt http.RoundTripper
}
func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
// set your auth headers here
r.Header.Set("Auth", t.apiKey)
return t.rt.RoundTrip(r)
}
Now you can use an instance of this type to set the Transport field on an http.Client.
var client *http.Client = // get client from somewhere...
// set the transport to your type
client.Transport = &MyTransport{apiKey: "secret", tr: client.Transport}
Depending on how and where from you got the client, it's possible that its Transport field is not yet set, so it might be a good idea to ensure that your type uses the default transport in such a case.
func (t *MyTransport) transport() http.RoundTripper {
if t.rt != nil {
return t.rt
}
return http.DefaultTransport
}
// update your method accordingly
func (t *MyTransport) RoundTrip(r *http.Request) (*http.Response, error) {
// set your auth headers here
r.Header.Set("Auth", t.apiKey)
return t.transport().RoundTrip(r)
}
It might be worth noting that the Go documentation recommends not to modify the *http.Request inside the RoundTrip method, so what you can do, and what the go-github package you linked to is doing, is to create a copy of the request, set the auth headers on it, and pass that to the underlying Transport. See here: https://github.com/google/go-github/blob/d23570d44313ca73dbcaadec71fc43eca4d29f8b/github/github.go#L841-L875

Dependency injection with http Handler in Go

I am trying to wrap my head around dependency injection in Go, but really stuck here. Here's a (drastically simplified) app which should serve as an example:
package main
import (
"net/http"
"github.com/gorilla/mux"
)
func main() {
mux := mux.NewRouter()
mux.Handle("/", myHandler()).Methods("GET")
http.ListenAndServe(":9000", mux)
}
type myObject interface {
Start()
}
type Object struct {
}
func (o *Object) Start() {
// Something wild here, for example sending out an email,
// query an external DB or something similar..
}
func myHandler() http.Handler {
// Inject myObject-like struct somewhere here?
o := Object{}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
o.Start()
w.Write([]byte("Started Object"))
})
}
I have a problem with testing the Object struct. What I usually do is create an interface which can be used in testing by setting up a test struct. For instance, if I had a DB connection handler, in testing I can create a mock which satisfies the handler interface and pass this to the "myHandler" call as a parmeter.
Unfortunately this only works if the struct is already instantiated when the "mux.Handle" call is made. I simply don't see any simple way to test the myHandler function with an Object struct which can be injected in tests, since it will be created after the handler gets called.
Any hints or ideas on how to get this done? Maybe I have to rethink my testing approach, but I would really like to unit-test the Object struct, but also test the http handler separately (as this handler may perform more tasks than just creating the Object).

Defining an interface method with interface return type

TLDR Here is a playground that demonstrates the issue if you try to run it: https://play.golang.org/p/myQtUVg1iq
I am making a REST API and have many types of resources that can be retrieved via a GET request
GET http://localhost/api/users
GET http://localhost/api/groups
I have a models package which abstracts how the different resources are implemented:
func(m *UserManager) Get() []Users {
// Internal logic, assume returns correct results
}
func(m *GroupManager) Get() []Groups {
// Internal logic, assume returns correct results
}
A routes file setups all the routes and handlers:
users := models.UserManager{}
groups := models.GroupManager{}
func GetUsersHandler (w http.ResponseWriter, r *http.Request) {
users := users.Get()
// Implementation details, writing to w as JSON
}
func GetGroupsHandler (w http.ResponseWriter, r *http.Request) {
groups := groups.Get()
// Implementation details, writing to w as JSON
}
func registerRoutes(r *mux.Router) {
r.handleFunc("/api/users", GetUsersHandler).Method("GET")
r.handleFunc("/api/groups", GetGroupsHandler).Method("GET")
}
I am trying to make this more generic by creating an interface and then only needing a single GetHandler. Something like this:
type Getter interface {
Get() []interface{}
}
func GetHandler(g Getter) {
return func(w http.ResponseWriter, r *http.Request) {
results := g.Get()
// Implementation details, writing to w as JSON
}
}
func registerRoutes(r *mux.Router) {
r.handleFunc("/api/users", GetHandler(&users)).Method("GET")
r.handleFunc("/api/groups", GetHandler(&groups)).Method("GET")
}
This is really close to working, the only problem is the return type from the models is a specific object type, but the interface just uses the interface return type. Is there any way to solve this without making the models return []interface{}?
https://play.golang.org/p/myQtUVg1iq
Try not to approach the problem like you would other OOP languages. You can't have covariant containers in Go, so you either have to use an empty interface{}, or you have to structure your program differently.
If your Get methods are different and you want to group types in an interface, use another method (sometimes we even have noop methods just for interfaces), or just pass in users or groups as an interface{}. You'll need to do a type switch or assertion at some point in the call chain anyway, and once you know what type it is you can handle it accordingly.
It's hard to tell without more code, but in this case, the easiest path may just be to have each type be an http.Handler itself, and it can dispatch accordingly.
I ended up avoiding this problem entirely and instead of trying to reduce the amount of code I am using the new go generate feature in Go 1.4 to create the code that is necessary for each resource.

Resources