I am using Google wire to wire up the dependencies in one of my Go projects. So far all the dependencies were wired up successfully.
I have following container:
type Container struct {
Logger logger.StructuredLogger
IDGenerator idgenerator.IDGenerator
Arango arangodb.Arango
}
Here StructuredLogger, IDGenerator and Arango are interface.
The container set is defined as below:
var mockContainerSet = wire.NewSet(
Container{},
ResolveStructuredLogger,
ResolveIDGenerator,
ResolveArangoDB,
)
Functions:
func ResolveStructuredLogger() logger.StructuredLogger {
panic(wire.Build(wire.InterfaceValue(new(logger.StructuredLogger), logrusprovider.NewLogrusLogger(nil))))
}
func ResolveIDGenerator() idgenerator.IDGenerator {
panic(wire.Build(wire.InterfaceValue(new(idgenerator.IDGenerator), examples.NewKSUIDGenerator())))
}
Now the issue is when I want to create a provider for Arango. The factory method for Arango provider is taking IDGenerator as an argument.
func NewMockedArango(generator idgenerator.IDGenerator) (*MockedArango, error){
if generator == nil {
return nil , errors.New("mock arangodb expects a valid IDGenerator")
}
return &MockedArango{generator:generator}, nil
}
All the functions from arangodb.Arango is implemented. And I tried the following to create a provider:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockdb.NewMockedArango(idGenerator))))
}
But the wire is throwing errors, I tried a lot of ways and no success to set this up. Any help?
Error:
2-valued mockdb.NewMockedArango(idGenerator) (value of type
(*github.com/tejashwi/catalog-manager-svc/pkg/webservice/arangodb/mockdb.MockedArango,
error)) where single value is expected
Signature of wire.Interface is
InterfaceValue(typ interface{}, x interface{})
Here you need to pass two single-valued arguments. In the second argument, you are passing 2-valued argument since function mockdb.NewMockedArango is returning 2 values.
You can change function ResolveMockArangoDB to:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
mockedArango, err := mockdb.NewMockedArango(idGenerator)
<HANDLE err>
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockedArango)))
}
Related
Recently, I stumbled upon an interesting article regarding the organization of Go code.
I was highly interested in the third approach: 'Independent Packages'.
It's said packages should NOT depend on each other, but defines their own interface to interact with
other packages.
Now, I face a problem with the approach.
I'm building a CLI applications that's able to execute various commands, one after each other.
It's important that these commands can share data.
First, I have built a contain in which I can register commands.
This resides in the package: commands.
import (
"sync"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// Command defines the API that a command should satisfy to become eligible for being registered in the container
type Command interface {
GetName() string
SetProperty(prop, val string) error
Execute(context) error
}
// Container exposes the API for registration and retrieving of services.
type Container struct {
mutex sync.RWMutex
registeredCommands map[string]Command
}
// Add adds the command `cmd` to the container.
func (c *Container) Add(cmd Command) {
c.withLock(func() {
if c.registeredCommands == nil {
c.registeredCommands = make(map[string]Command)
}
c.registeredCommands[cmd.GetName()] = cmd
})
}
// Get retrieves the command who's name is equal to `name`.
func (c *Container) Get(name string) Command {
return c.withRLock(func() Command {
if c.registeredCommands == nil {
return nil
}
if cmd, ok := c.registeredCommands[name]; ok {
return cmd
}
return nil
})
}
// PRIVATE: Create a lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withLock(handler func()) {
c.mutex.Lock()
defer c.mutex.Unlock()
handler()
}
// PRIVATE: Create a "read" lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withRLock(handler func() Command) Command {
c.mutex.RLock()
defer c.mutex.RUnlock()
return handler()
}
Next I have command which implements the Command interface.
NOTE: This is stored in another package: rest.
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// GetAPIKey is the command that allows the retrieval of a API key using the REST API.
type GetAPIKey struct {
}
// GetName returns the name of the command `cmd`.
func (GetAPIKey) GetName() string {
return "API.REST.getAPIKey"
}
// SetProperty assigns value `val` to property `prop`.
func (cmd *GetAPIKey) SetProperty(prop, val string) error {
return nil
}
// Execute runs the command.
func (cmd GetAPIKey) Execute(ctx context) error {
ctx.SetAPIKey(xmlResponse.Key)
return nil
}
The MAIN entry point of the application is defined as follows:
// PRIVATE: Defines the container in which all the available commands are registered.
var container commands.Container
// PRIVATE: Defines an API for sharing data between different commands.
type context struct {
apiKey string
}
func (ctx *context) SetAPIKey(value string) {
ctx.apiKey = value
}
// PRIVATE: Represents the 'MAIN' entry point for the application.
// This method is executed when the application is started.
func main() {
ctx := &context{}
container := new(commands.Container)
container.Add(&rest.GetAPIKey{})
command := container.Get("API.REST.getAPIKey")
command.SetProperty("baseURI", "")
command.SetProperty("environmentNameOrURL", "")
command.SetProperty("userName", "")
command.SetProperty("password", "")
err := command.Execute(ctx)
if err != nil {
fmt.Println(err)
}
}
However this code does NOT compile.
It yields the following error:
.\app.go:53:16: cannot use &rest.GetAPIKey literal (type *rest.GetAPIKey) as type commands.Command in argument to container.Add:
*rest.GetAPIKey does not implement commands.Command (wrong type for Execute method)
have Execute(rest.context) error
want Execute(commands.context) error
Is there any way to make this code work?
So the context interface is defined in 2 places.
This will not work because the function signatures don't match. You can do:
Make Context a shared interface, so every package can use it. This does not necessarily introduce dependencies between packages using it.
Use context.Context, and expect to see the key in there. This is essentially the same as #1 but using a known predefined type
Use interface{} instead of context, and use type assertion to convert it to the local context type.
I have code in Go, which includes many execution methods.
Each method receives it own struct of parameters.
I want a dispatcher to call each method with its related struct.
The dispatcher receives the name of the execution method, and a JSON of the parameters struct.
Then, it uses reflection to build the struct, and calls the method.
The problem is I get compilation error unless I create the execution methods using empty interface.
In the example below I have 2 execution methods: api1 is compiling, but is using the empty interface and explicit casting. api2 is what I want to do, but it is failing with compile error:
cannot use api2 (type func(Api2Parameters)) as type Api in assignment
How can I make api2 usage compile?
import (
"encoding/json"
"log"
"reflect"
)
type Api func(arguments interface{})
type ApiDetails struct {
Executor *Api
ParametersType reflect.Type
}
var Apis map[string]*ApiDetails
func RunApi(apiName string, data string) {
api := Apis[apiName]
parameters := reflect.New(api.ParametersType).Interface().(interface{})
_ = json.Unmarshal([]byte(data), parameters)
(*api.Executor)(parameters)
}
type Api1Parameters struct {
Count1 int
Id1 string
}
func api1(arguments interface{}) {
parameters, _ := arguments.(*Api1Parameters)
log.Printf("api1 parameters(%+v)", parameters)
}
type Api2Parameters struct {
Count2 int
Id2 string
}
func api2(arguments Api2Parameters) {
log.Printf("api2 parameters(%+v)", arguments)
}
func Test() {
// this assignment works fine
var api_1 Api = api1
Apis["api1"] = &ApiDetails{
Executor: &api_1,
ParametersType: reflect.TypeOf(Api1Parameters{}),
}
// this assignment produce compile error
var api_2 Api = api2
Apis["api2"] = &ApiDetails{
Executor: &api_2,
ParametersType: reflect.TypeOf(Api2Parameters{}),
}
RunApi("api1", `{"Count1":19, "Id1":"a"}`)
RunApi("api2", `{"Count2":34, "Id2":"b"}`)
}
Create a value using the argument type, unmarshal to that value and invoke the function:
var Apis = map[string]interface{}{
"api1": api1,
"api2": api2,
}
func RunApi(apiName string, data string) {
fv := reflect.ValueOf(Apis[apiName])
ft := fv.Type()
pin := reflect.New(ft.In(0))
_ = json.Unmarshal([]byte(data), pin.Interface())
fv.Call([]reflect.Value{pin.Elem()})
}
Run it on the playground.
If you want to keep this type-safe, you can push the JSON-to-args conversion a little deeper:
type Api func(string)
type ApiDetails struct {
// Don't use *Api, that's a function pointer-pointer
Executor Api
}
Then, use a closure to do the JSON translation:
Apis["api1"] = &ApiDetails{
Executor: func(data string) {
var args ArgsForAPI1
json.Unmarshal([]byte(string),&args)
api1Implem(args)
}
}
With this, you can do:
APIs["api1"].Executor( `{"Count1":19, "Id1":"a"}`)
You can change the RunApi to do something like this.
A second option is to do this using reflection:
type ApiDetails struct {
// Executor can accept any function signature
// Assume a function with a single parameter
Executor interface{}
ParametersType reflect.Type
}
Then, the RunApi function can use reflection to construct a struct and call Executor:
parameters := reflect.New(api.ParametersType)
_ = json.Unmarshal([]byte(data), parameters.Interface{})
reflect.ValueOf(api.Executor).Call([]reflect.Value{parameters})
This should work, but you'll only get runtime errors if you mess up.
I have a below function tryGet() to unit test:
type config struct {
Key string `json:"key"`
Client todo.Client `json:"client"`
}
var instance *config
func (c *config) tryGet() error {
client := &http.Client{}
tClient := Client{"http://url", client}
configValues := config{"Key", tClient}
Instance := &configValues
err := Instance.Client.perform("GET", header)
return nil
}
// External library in package named "todo" has the below structs and functions
package todo
type Client struct {
BaseURL string
HTTPClient *http.Client
}
func (client *Client) perform() error {
return nil
}
I am finding a hard time to mock the Client and perforn in external package todo
If the external library is not under your control, which I assume is the case, then you should assume that the code within is tested, therefore you should create the boundary at a point that you have control of the code.
To do this you should create the interface at the config struct boundary.
type ClientInterface interface {
perform() error
}
type config struct {
Url string `json:"url"`
Client ClientInterface `json:"client"`
}
var instance *config
func (c *config) tryGet() error {
err := c.Client.perform("GET", header)
return nil
}
By doing it this way, you don't care about testing the lower level code base and you just care that this module has a perform function and that given certain conditions your code behaves correctly.
You can then create a mock todo.Cient struct that you can replace the normal one with and have it return all sorts of things and behaviors to test your code.
You can mock the function as follow
type myImpl todo.Client
func (client *myImpl) perform() error {
// do what you want to assert in the test
return nil
}
And then you will use myImpl whenever you have to use todo.Client
if you are using a function with a parameter of type todo.Client, it will not work if you pass an argument of type myImpl. It will throw an error:
cannot use client (type myImpl) as type todo.Client in field value
To solve this issue, an interface can be created
type Client interface {
perform() error
}
Now the type Client should replace the type todo.Client of the function to be unit tested.
type config struct {
Url string `json:"url"`
Client Client `json:"client"`
}
With this change the above code which supply an implementation myImpl of the interface Client should work
I'm really new to mocking third-party library in go, I'm mocking cloud.google.com/go/storage right now
I'm using mockery. This is my current interface:
//Client storage client
type Client interface {
Bucket(name string) BucketHandle
Buckets(ctx context.Context, projectID string) BucketIterator
}
//BucketHandle storage's BucketHandle
type BucketHandle interface {
Attrs(context.Context) (*storage.BucketAttrs, error)
Objects(context.Context, *storage.Query) ObjectIterator
}
//ObjectIterator storage's ObjectIterator
type ObjectIterator interface {
Next() (*storage.ObjectAttrs, error)
}
//BucketIterator storage's BucketIterator
type BucketIterator interface {
Next() (*storage.BucketAttrs, error)
}
and this is how I use it in my function
//Runner runner for this module
type Runner struct {
StorageClient stiface.Client
}
.... function
//get storage client
client, err := storage.NewClient(ctx)
if err != nil {
return err
}
runner := Runner{
StorageClient: client,
}
.... rest of functions
However, I got this error:
cannot use client (type *"cloud.google.com/go/storage".Client) as type stiface.Client in field value:
*"cloud.google.com/go/storage".Client does not implement stiface.Client (wrong type for Bucket method)
have Bucket(string) *"cloud.google.com/go/storage".BucketHandle
want Bucket(string) stiface.BucketHandle
What have I done wrong here? Thanks!
Edit
here's one example of the code that I want to mock. I'd like to mock on bucketIterator.Next():
//GetBuckets get list of buckets
func GetBuckets(ctx context.Context, client *storage.Client, projectName string) []checker.Resource {
//Get bucket iterator based on a project
bucketIterator := client.Buckets(ctx, projectName)
//iterate over the buckets and store bucket details
buckets := make([]checker.Resource, 0)
for bucket, done := bucketIterator.Next(); done == nil; bucket, done = bucketIterator.Next() {
buckets = append(buckets, checker.Resource{
Name: bucket.Name,
Type: "Bucket",
})
}
return buckets
}
The error message is basically saying your stiface.Client defines an interface that *storage.Client does not implement. On first glance your code looks valid however the problem lies in your interface method signatures and because they have outputs as interfaces.
Go makes a difference between the statements:
This function returns a BucketHandle
and this function returns a *storage.BucketHandle that is a BucketHandle
Try changing your interface to return the *storage.BucketHandle. You can see a more complex example of similar behaviour in the mockery S3API example where the functions return the s3 types, not their own interfaces.
After some trial and error, the way you'd use stiface is as below
If you need to mock stiface.BucketIterator, you can create a mock as
type mockBucketIterator struct {
stiface.BucketIterator
}
and mock the Next accordingly
func (m mockBucketIterator) Next() (*storage.BucketAttrs, error) {
// mocks that you need this to return
return
}
You could use the same method to mock all the way up to satiface.Client and feed the mock client to your test.
For reference, a full example in my tests:
type clientMock struct {
stiface.Client
}
type bucketMock struct {
stiface.BucketHandle
}
type objectItMock struct {
stiface.ObjectIterator
i int
next []storage.ObjectAttrs
}
func (m clientMock) Bucket(name string) stiface.BucketHandle {
return bucketMock{}
}
and then the object iterator to return mocked iterator as well
func (it *objectItMock) Next() (a *storage.ObjectAttrs, err error) {
if it.i == len(it.next) {
err = iterator.Done
return
}
a = &it.next[it.i]
it.i += 1
return
}
func (m bucketMock) Objects(ctx context.Context, q *storage.Query) (it stiface.ObjectIterator) {
it = &objectItMock{
i: 0,
next: []storage.ObjectAttrs{
{Name: "abc"},
{Name: "def"},
{Name: "ghi"},
},
}
return
}
I have the following code:
package vault
type Client interface {
GetHealth() error
}
func (c DefaultClient) GetHealth () error {
resp := &VaultHealthResponse{}
err := c.get(resp, "/v1/sys/health")
if err != nil {
return err
}
return nil;
}
Now, I want to use this function as part of this struct:
type DependencyHealthFunction func() error
type Dependency struct {
Name string `json:"name"`
Required bool `json:"required"`
Healthy bool `json:"healthy"`
Error error `json:"error,omitempty"`
HealthFunction DependencyHealthFunction
}
Basically, set the value of HealthFunction to GetHealth. Now, when I do the following:
func (config *Config) GetDependencies() *health.Dependency {
vaultDependency := health.Dependency{
Name: "Vault",
Required: true,
Healthy: true,
HealthFunction: vault.Client.GetHealth,
}
temp1 := &vaultDependency
return temp1;
}
This gives me an error and it says cannot use vault.Client.GetHealth (type func(vault.Client) error) as type health.DependencyHealthFunction in field value. How can I do this?
Edit: How DependencyHealthFunction is used?
As its part of Dependency struct, it's simply used as following: d.HealthFunction() where d is a variable of type *Dependency.
This is abstract:
HealthFunction: vault.Client.GetHealth,
If we were to call HealthFunction(), what code do you expect to run? vault.Client.GetHealth is just a promise that such a function exists; it isn't a function itself. Client is just an interface.
You need to create something that conforms to Client and pass its GetHealth. For example, if you had a existing DefaultClient such as:
defaultClient := DefaultClient{}
Then you could pass its function:
HealthFunction: defaultClient.GetHealth,
Now when you later call HealthFunction() it will be the same as calling defaultClient.GetHealth().
https://play.golang.org/p/9Lw7uc0GaE
I believe the issue is related to understanding how interfaces are treated in Go.
An interface simply defines a method or set of methods that a particular type must satisfy to be considered as "implementing" the interface.
For example:
import "fmt"
type Greeter interface {
SayHello() string
}
type EnglishGreeter struct{}
// Satisfaction of SayHello method
func (eg *EnglishGreeter) SayHello() string {
return "Hello"
}
type SpanishGreeter struct{}
func (sg *SpanishGreeter) SayHello() string {
return "Ola"
}
func GreetPerson(g Greeter) {
fmt.Println(g.SayHello())
}
func main() {
eg := &EnglishGreeter{}
sg := &SpanishGreeter{}
// greet person in english
GreetPerson(eg)
// greet person in spanish
GreetPerson(sg)
}
You can add this behavior into a custom struct by simply having a Greeter field inside the struct. ie
type FrontEntrance struct {
EntranceGreeter Greeter
}
fe := &FrontEntrance { EntranceGreeter: &EnglishGreeter{} }
// then call the SayHello() method like this
fe.EntranceGreeter.SayHello()
Interfaces in golang are useful for composing common expected behavior for types based on the methods that they satisfy.
Hope this helps.