What is the best way to have dependency injection in Golang - go

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.

Related

Creating Per-Provider Loggers in Wire Dependency Injection

I'm using github.com/google/wire for dependency injection in an open source example project that I'm working on.
I have the following interfaces in a package named interfaces:
type LoginService interface {
Login(email, password) (*LoginResult, error)
}
type JWTService interface {
Generate(user *models.User) (*JWTGenerateResult, error)
Validate(tokenString string) (*JWTValidateResult, error)
}
type UserDao interface {
ByEmail(email string) (*models.User, error)
}
I have implementations that look like this:
type LoginServiceImpl struct {
jwt interfaces.JWTService
dao interfaces.UserDao
logger *zap.Logger
}
func NewLoginService(jwt interfaces.JWTService, dao interfaces.UserDao, \
logger *zap.Logger) *LoginServiceImpl {
return &LoginServiceImpl{jwt: jwt, dao: dao, logger: logger }
}
type JWTServiceImpl struct {
key [32]byte
logger *zap.Logger
}
func NewJWTService(key [32]byte, logger *zap.Logger) (*JWTServiceImpl, error) {
r := JWTServiceImpl {
key: key,
logger: logger,
}
if !r.safe() {
return nil, fmt.Errorf("unable to create JWT service, unsafe key: %s", err)
}
return &r, nil
}
type UserDaoImpl struct {
db: *gorm.DB
logger: *zap.Logger
}
func NewUserDao(db *gorm.DB, logger *zap.Logger) *UserDao {
return &UserDaoImpl{ db: db, logger: logger }
}
I'll exclude other factory functions and implementations here because they all look very similar. They may return an error or be infallible.
I have one other interesting factory for creating the database connection, which I'll just show the interface and not the implementation:
func Connect(config interfaces.MySQLConfig) (*gorm.DB, error) { /* ... */ }
Now, onto the problem. In my command-line entry-point, I'm creating a logger:
logger, err := zap.NewDevelopment()
For each of the factory methods above, I need to provide a logger and not the same logger instance, rather as if these methods were called as follows:
logger, err := zap.NewDevelopment()
// check err
db, err := database.Connect(config)
// check err
userDao := dao.NewUserDao(db, logger.Named("dao.user"))
jwtService, err := service.NewJWTService(jwtKey)
// check err
loginService := service.NewLoginService(jwtService, userDao, logger.Named("service.login"))
My wire.ProviderSet construction looks like this:
wire.NewSet(
wire.Bind(new(interfaces.LoginService), new(*service.LoginServiceImpl)),
wire.Bind(new(interfaces.JWTService), new(*service.JWTServiceImpl)),
wire.Bind(new(interfaces.UserDao), new(*dao.UserDaoImpl)),
service.NewLoginService,
service.NewJWTService,
dao.NewUserDao,
database.Connect,
)
I've read through the user guide, the tutorial, and best practices, and I can't seem to find a way to route a unique zap.Logger to each of these factory methods, and routing a random [32]byte for the JWT service.
Since my root logger is not created at compile time, and since each of these factory methods needs its own unique logger, how do I tell wire to bind these instances to the corresponding factory methods? I'm having a tough time seeing how to route custom instances of the same type to disparate factory methods.
In summary:
Wire seems to favor doing everything at compile-time, storing the dependency injection configuration in a static package-level variable. For most of my use-case, this is okay.
For the rest of my use-case, I need to create some instances manually before running the dependency injection and the ability to route various *zap.Logger instances to each service that needs it.
Essentially, I need to have wire do services.NewUserDao(Connect(mysqlConfig), logger.Named("dao.user"), but I don't know how to express this in wire and merge variables at runtime with wire's compile-time approach.
How do I do this in wire?
I had to change what I was doing somewhat, as is recommended in the documentation:
If you need to inject a common type like string, create a new string type to avoid conflicts with other providers. For example:
type MySQLConnectionString string
Adding Custom Types
The documentation is admittedly very terse, but what I ended up doing is creating a bunch of types:
type JWTKey [32]byte
type JWTServiceLogger *zap.Logger
type LoginServiceLogger *zap.Logger
type UserDaoLogger *zap.Logger
Updating Producer Functions
I updated my producer methods to accept these types, but did not have to update my structs:
// LoginServiceImpl implements interfaces.LoginService
var _ interfaces.LoginService = (*LoginServiceImpl)(nil)
type LoginServiceImpl struct {
dao interfaces.UserDao
jwt interfaces.JWTService
logger *zap.Logger
}
func NewLoginService(dao interfaces.UserDao, jwt interfaces.JWTService,
logger LoginServiceLogger) *LoginServiceImpl {
return &LoginServiceImpl {
dao: dao,
jwt: jwt,
logger: logger,
}
}
This above part made sense; giving distinct types meant that wire had less to figure out.
Creating an Injector
Next, I had to create the dummy injector and then use wire to generate the corresponding wire_gen.go. This was not easy and very unintuitive. When following the documentation, things kept breaking and giving me very unhelpful error messages.
I have a cmd/ package and my CLI entrypoint lives in cmd/serve/root.go, which is run as ./api serve from the command-line. I created my injector function in cmd/serve/injectors.go, note that // +build wireinject and the following newline are required to inform Go that this file is used for code generation and not code itself.
I ultimately arrived at the following code after much trial and error:
// +build wireinject
package serve
import /*...*/
func initializeLoginService(
config interfaces.MySQLConfig,
jwtKey service.JWTKey,
loginServiceLogger service.LoginServiceLogger,
jwtServiceLogger service.JWTServiceLogger,
userDaoLogger service.UserDaoLogger,
databaseLogger database.DatabaseLogger,
) (interfaces.LoginService, error) {
wire.Build(
// bind interfaces to implementations
wire.Bind(new(interfaces.LoginService), new(*service.LoginServiceImpl)),
wire.Bind(new(interfaces.JWTService), new(*service.JWTServiceImpl)),
wire.Bind(new(interfaces.UserDao), new(*dao.UserDao)),
// services
service.NewLoginService,
service.NewJWTService,
// daos
dao.NewUserDao,
// database
database.Connect,
)
return nil, nil
}
The wire.Bind calls inform wire which implementation to use for a given interface so it will know that service.NewLoginService which returns a *LoginServiceImpl should be used as the interfaces.LoginService.
The rest of the entities in the call to wire.Build are just factory functions.
Passing Values to an Injector
One of the the issues I ran into was that I was trying to pass values into wire.Build like the documentation describes:
Occasionally, it is useful to bind a basic value (usually nil) to a type. Instead of having injectors depend on a throwaway provider function, you can add a value expression to a provider set.
type Foo struct {
X int
}
func injectFoo() Foo {
wire.Build(wire.Value(Foo{X: 42}))
return Foo{}
}
...
It's important to note that the expression will be copied to the injector's package; references to variables will be evaluated during the injector package's initialization. Wire will emit an error if the expression calls any functions or receives from any channels.
This is what confused me; it sounded like you could only really use constant values when trying to run an injector, but there are two lines in the docs in the "injectors" section:
Like providers, injectors can be parameterized on inputs (which then get sent to providers) and can return errors. Arguments to wire.Build are the same as wire.NewSet: they form a provider set. This is the provider set that gets used during code generation for that injector.
These lines are accompanied by this code:
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
wire.Build(foobarbaz.MegaSet)
return foobarbaz.Baz{}, nil
}
This is what I missed and what caused me to lose a lot of time on this. context.Context doesn't seem to be passed anywhere in this code, and it's a common type so I just kind of shrugged it off and didn't learn from it.
I defined my injector function to take arguments for the JWT key, the MySQL config, and the logger types:
func initializeLoginService(
config interfaces.MySQLConfig,
jwtKey service.JWTKey,
loginServiceLogger service.LoginServiceLogger,
jwtServiceLogger service.JWTServiceLogger,
userDaoLogger service.UserDaoLogger,
databaseLogger database.DatabaseLogger,
) (interfaces.LoginService, error) {
// ...
return nil, nil
}
Then, I attempted to inject them into wire.Build:
wire.Build(
// ...
wire.Value(config),
wire.Value(jwtKey),
wire.Value(loginServiceLogger),
// ...
)
When I attempted to run wire, it complained that these types were defined twice. I was very confused by this behavior, but ultimately learned that wire automatically sends all function parameters into wire.Build.
Once again: wire automatically sends all injector function parameters into wire.Build.
This was not intuitive to me, but I learned the hard way that it's the way wire works.
Summary
wire does not provide a way for it to distinguish values of the same type within its dependency injection system. Thus, you need to wrap these simple types with type definitions to let wire know how to route them, so instead of [32]byte, type JWTKey [32]byte.
To inject live values into your wire.Build call, simply change your injector function signature to include those values in the function parameters and wire will automatically inject them into wire.Build.
Run cd pkg/my/package && wire to create wire_gen.go in that directory for your defined injectors. Once this is done, future calls to go generate will automatically update wire_gen.go as changes occur.
I have wire_gen.go files checked-in to my version control system (VCS) which is Git, which feels weird due to these being generated build artifacts, but this seems to be the way that this is typically done. It might be more advantageous to exclude wire_gen.go, but if you do this, you'll need to find every package which includes a file with a // +build wireinject header, run wire in that directory, and then go generate just to be sure.
Hopefully this clears up the way that wire works with actual values: make them type safe with type wrappers, and simply pass them to your injector function, and wire does the rest.

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

Mock interface return type for dep injection

EDIT
As pointed out in the accepted answer, the issue here was doing go duck typing in the wrong direction. I'd like to add the following github issue as an attachment, since it provided me useful information in addition to #matt answer below:
https://github.com/golang/mock/issues/316#issuecomment-512043925
ORIGINAL POST
I'm new to dependencies injection, and wanted to test it on a module that uses couchbase go sdk. For this purpose I need interfaces to reproduce both Cluster and Bucket structures.
On the Cluster interface I need the Bucket() method, which has the following signature:
func (c *gocb.Cluster) Bucket(bucketName string) *gocb.Bucket
I also need the two following methods from the Bucket interface:
func (b *gocb.Bucket) Collection(collectionName string) gocb.*Collection
func (b *gocb.Bucket) DefaultCollection() *gocb.Collection
The tricky part is that both Cluster and Bucket methods have pointer receivers. This isn't hard in itself, since I know how to mock such methods alone (you just need to use a pointer to the type that implements the interface).
The issue is that one of the Cluster methods needs to return a pointer that implements the Bucket interface, since it also has pointer receivers methods. I tried many combinations, but each time I use an non-mocked *gocb.Cluster value as an argument to one of my functions, it fails because the Bucket method on the cluster instance isn't implemented correctly by the instance.
Below is my latest attempt:
package deps
import (
"github.com/couchbase/gocb/v2"
)
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) *Bucket
}
// Bucket mocks the gocb.Bucket interface.
type Bucket interface {
Collection(collectionName string) *gocb.Collection
DefaultCollection() *gocb.Collection
}
The linter then returns the following error whenever I try to use an actual gocb.Cluster value:
I also tried to replace the Bucket method signature in my Database interface with:
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) Bucket
}
Which again gives me the following lint error:
How can I implement an interface to mock both methods ?
I think the key concept that you're missing is that the mock object has to match the interface requirements of what you're mocking. That includes the parameters and return values of the methods.
type Database interface {
// Bucket(bucketName string) *Bucket // Wrong
Bucket(bucketName string) *gocb.Bucket // Correct
}
You can still use the return value of Database.Bucket as a deps.Bucket, given that you've also mocked that interface properly.
Unless I'm missing something about your testing process, this should do what you need.
package main
import (
"github.com/couchbase/gocb/v2"
)
// Database mocks the gocb.Cluster interface.
type Database interface {
Bucket(bucketName string) *gocb.Bucket
}
// Bucket mocks the gocb.Bucket interface.
type Bucket interface {
Collection(collectionName string) *gocb.Collection
DefaultCollection() *gocb.Collection
}
func someFunc(db Database) *gocb.Bucket {
return db.Bucket("")
}
func anotherFunc(bucket Bucket) {
bucket.Collection("")
}
func main() {
var cluster *gocb.Cluster
bucket := someFunc(cluster)
anotherFunc(bucket)
}

How to mock nested third party dependencies?

Example third party API usage:
fund doSomething(api *api.Client) {
...
result, err := api.Logical().Write(val1, val2)
...
}
I can handle the initial call to Logical() with my own interface:
type API interface {
Logical() *api.Logical
}
...
doSomething(&MockAPI{}) // Assuming MockAPI implements API
However, this now brings the problem into sight: the Logical() method has to have the above function signature, otherwise, I couldn't substitute the real api object in for my interface. Since the *api.Logical type is nested within the third party API library, I cannot simply mock it out with another interface:
type Writer interface {
Write(string, string) Result, error
}
type API interface {
Logical() *Writer
}
...
doSomething(&api.Client{}) // Doesn't implement Logical() *Writer
How would I mock this API call out so I can return custom data and not hit a live service? If it helps, this is based on an actual API.
I think the best answer here is to just avoid the chained function call altogether.
type Writer interface {
Write(string, string) Result, error
}
func doSomething(writer Writer) {
...
result, err := writer.Write(val1, val2)
...
}
...
doSomething(api.Client.Logical())
Now I can implement my own Writer and use it for mocking.

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

Resources