How to convert interface to another interface which it implements? - go

In short - I would like to be able to cast an interface type whose underlying type implements a specific interface to that specific interface.
I am using the plugin package to lookup a New function which looks like so (I have many others the same):
func NewDomainPrimaryKey() any { return DomainPrimaryKey{} }
(This is generated at run-time so I can't just reference it as DomainPrimaryKey)
My lookup and call is like so:
plugin, err := plugin.Open("my-plugin")
if err != nil {
return err
}
symGet, err := plugin.Lookup("New" + pluginName)
if err != nil {
return err
}
newGenModel, ok := symGet.(func() any)
if !ok {
return errors.New("unexpected type from module symbol")
}
anyGenModel := newGenModel()
genModel, ok := anyGenModel.(GenModel) // **this is where the problem is
if !ok {
return errors.New("unexpected type from module symbol")
}
genModelInstance := genModel.Get()
In the above I am trying to cast 'anyGenModel' (an interface) to the 'GenModel' interface which it implements, however, this doesn't work.
I am certain it implements this interface because when I do the following, I get no errors.
type GenModel interface {
Get() any
TableName() string
}
var _ GenModel = (*DomainPrimaryKey)(nil) // this doesn't complain
How can I do this? I found this article which I don't think is what I am looking for but seems similar.
Thanks in advance for any help on this - this has become a real blocker for me.

If the underlying type implements both interfaces, this is very straightforward:
package main
import "fmt"
type IFace1 interface {
DoThis()
}
type IFace2 interface {
DoThat()
}
type impl struct{}
func (i *impl) DoThis() {
fmt.Println("I implement IFace1")
}
func (i *impl) DoThat() {
fmt.Println("I implement IFace2")
}
func GetIFace1() IFace1 {
return &impl{}
}
func main() {
i1 := GetIFace1()
i1.DoThis()
i2 := i1.(IFace2)
i2.DoThat()
}
playground
If your code is not working, then I would begin by questioning your assertion that the underlying type of anyGenModel actually implements GenModel and work from there.

Related

Go lang type assertion

I am trying to do dependency injection in golang with applying dependency inversion principle, so I have the following service
package account
import (
types "zaClouds/modules/account/domain/types"
"zaClouds/modules/shared"
)
type IPlanDomainService interface {
GetUsagePlanById(string) *shared.Result[types.UsagePlan]
}
type PlanDomainService struct {
usagePlanService types.IUsagePlanService
}
func (planDomainService *PlanDomainService) GetUsagePlanById(id string) *shared.Result[types.UsagePlan] {
result := &shared.Result[types.UsagePlan]{}
usagePlanResult := planDomainService.usagePlanService.GetPlanById(id)
if usagePlanResult.Err != nil {
result.Err = usagePlanResult.Err
return result
}
result.Data = usagePlanResult.Data
return result
}
func PlanDomainServiceFactory(usagePlanService types.IUsagePlanService) IPlanDomainService {
return &PlanDomainService{usagePlanService: usagePlanService}
}
as you can see, it accepts another service with type IUsagePlanService
and here is the interface for it
package account
import (
"zaClouds/modules/shared"
"github.com/shopspring/decimal"
)
type UsagePlan struct {
ID string
Title string
Description interface{}
PlanID string
Price decimal.Decimal
Duration int
Features map[string]map[string]string
}
type IUsagePlanService interface {
GetPlanById(string) *shared.Result[UsagePlan]
}
and here is the way I am injecting this service to domain service
func DiInit(usagePlanService interface{}) domainServices.IPlanDomainService {
domainServices.PlanDomainServiceFactory(types.IUsagePlanService(usagePlanService))
return domainServices.PlanDomainServiceFactory(usagePlanService.(types.IUsagePlanService))
}
as you can see, I am trying to do a type assertion but it doesn't work, and gives me the following error:
panic: interface conversion: *usagePlan.UsagePlanRepository is not account.IUsagePlanService: missing method GetPlanById
Edit
Here is the actual implementation for usagePlanService
type IUsagePlanRepository interface {
createClient(string) *http.Request
GetPlanById(string) *shared.Result[usagePlanRepoModels.UsagePlan]
}
type UsagePlanRepository struct {
plansEndpoint string
httpClient *http.Client
}
func (r *UsagePlanRepository) GetPlanById(id string) *shared.Result[usagePlanRepoModels.UsagePlan] {
result := &shared.Result[usagePlanRepoModels.UsagePlan]{}
req := r.createClient(id)
resp, err := r.httpClient.Do(req)
if err != nil {
log.Println("failed to load plan details \n[ERROR]", err)
result.Err = err
return result
}
defer func() {
bodyError := resp.Body.Close()
if bodyError != nil {
result.Err = bodyError
}
}()
if result.Err != nil {
return result
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
utils.Logger.Error("failed to load plan details \n[ERROR]", err, nil)
result.Err = err
return result
}
if resp.StatusCode >= 400 {
result.Err = errors.New(string(body))
utils.Logger.Info("getPlanById", string(body))
}
getUsagePlanResponse, foundError := usagePlanRepoModels.CreateGetUsagePlanResponse(body)
if foundError != nil {
result.Err = foundError
return result
}
result.Data = *getUsagePlanResponse
return result
}
When using an interface, you need to define all functions that you will use with the same name and signature as the implementation.
The error message you got indicates that the implementation and the interface are different.
The implementation is not shown in your question, but you defined the function for your interface like this: GetPlanById(string) *shared.Result[UsagePlan]. Any deviation from it will result in error. One common mistake is with the pointers. Adding or removing the * to the return type will incur in error if it differs from the original.
Edit:
Your interface should look like this:
type IUsagePlanService interface {
GetPlanById(id string) *shared.Result[usagePlanRepoModels.UsagePlan]
}
If your function is returning a private type, and you can change that, you should. If you cant change it, than you should create a function that wraps the function you are trying to abstract with the interface.

How to create a mock inside the function under test

I have a set of functions, which uses the pool of objects. This pool has been mocked. It works fine in most of the cases. But in some functions i call the methods of objects from the pool. So i need to mock this objects too.
Lets say:
// ObjectGeter is a interface that is mocked
type ObjectGeter interface {
GetObject(id int) ObjectType, error
}
// this function is under test
func SomeFunc(og ObjectGeter,id int, otherArgument SomeType) error {
// some actions with otherArgument
// and may be return an error
obj, err := og.GetObject(id)
if err !=nil {
return errors.New("GetObject error")
}
rezult, err := obj.SomeMethod()
if err !=nil {
return errors.New("One of internal errors")
}
return rezult, nil
}
Is there a way to test whole this function? I can create interface SomeMethoder which wraps the SomeMethod(), but i can't find the way how to assign it to obj inside SomeFunc without changing the signature of GetObject to GetObject(id int) SomeMethoder,error.
Currently i see the one approach - testing by a parts.
The only solution i'v found without of changing of paradigm is a wrapper. It is pretty trivial but may be some one will need it once.
Originally i have some type:
type PoolType struct {...}
func (p *PoolType)GetObject(id int) (ObjectType, error) {...}
and interface, that wraps PoolType.GetObject and that i'v mocked.
Now i have the interface:
type SomeMethoder interface {
SomeMethod() (ResultType, error)
}
to wrap object returned by PoolType.GetObject().
To produce it i have interface:
type ObjectGeter interface {
GetObject(id int) (SomeMethoder, error)
}
and type
type MyObjectGeter struct {
pool *PoolType
}
func New(pool *PoolType) *MyObjectGeter {
return &MyObjectGeter{pool: pool}
}
func (p *MyObjectGeter)GetObject(id int) (SomeMethoder, error) {
return p.pool.GetObject(id)
}
that implements it.
So:
// this function is under test
func SomeFunc(og ObjectGeter,id int, otherArgument SomeType) error {
// some actions with otherArgument
// and may be return an error
iface, err := og.GetObject(id)
if err !=nil {
return errors.New("GetObject error")
}
rezult, err := iface.SomeMethod()
if err !=nil {
return errors.New("One of internal errors")
}
return rezult, nil
}
is called by
og := New(pool)
SomeFunc(og,id,otherArgument)
in real work.
After all to test whole SomeFunc i have to:
func TestSomeFuncSuccess (t *testing.T) {
controller := gomock.NewController(t)
defer controller.Finish()
objectGeter := mocks.NewMockObjectGeter(controller)
someMethoder := mocks.NewMockSomeMethoder(controller)
gomock.InOrder(
args.objectGeter.EXPECT().
GetObject(correctIdCOnst).
Return(someMethoder, nil),
args.someMethoder.EXPECT().
SomeMethod().
Return(NewResultType(...),nil).
Times(args.test.times[1]),
)
result, err := SomeFunc(objectGeter,correctIdCOnst,otherArgumentConst)
// some checks
}
So, the only untested part is MyObjectGeter.GetObject that is enough for me.

Returning a Mock from a package function

I'm fairly new to Go and I'm having some issues with writing tests, specifically mocking the response of a package function.
I'm writing an wrapper lib for github.com/go-redis/redis. At the moment it only really has better errors for failures, but it will be expanded with statsd tracking further down the line, but I digress...
I have the following go package that I have created
package myredis
import (
"time"
"github.com/go-redis/redis"
errors "github.com/pkg/errors"
)
var newRedisClient = redis.NewClient
// Options - My Redis Connection Options
type Options struct {
*redis.Options
DefaultLifetime time.Duration
}
// MyRedis - My Redis Type
type MyRedis struct {
options Options
client *redis.Client
}
// Connect - Connects to the Redis Server. Returns an error on failure
func (r *MyRedis) Connect() error {
r.client = newRedisClient(&redis.Options{
Addr: r.options.Addr,
Password: r.options.Password,
DB: r.options.DB,
})
_, err := r.client.Ping().Result()
if err != nil {
return errors.Wrap(err, "myredis")
}
return nil
}
My problem is that I want redis.NewClient to return a mock. This is the test code that I wrote, but it's not working:
package myredis
import (
"testing"
"github.com/go-redis/redis"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type redisStatusCmdMock struct {
mock.Mock
}
func (m *redisStatusCmdMock) Result() (string, error) {
args := m.Called()
return args.Get(0).(string), args.Error(1)
}
type redisClientMock struct {
mock.Mock
}
func (m *redisClientMock) Ping() redis.StatusCmd {
args := m.Called()
return args.Get(0).(redis.StatusCmd)
}
func TestConnect(t *testing.T) {
assert := assert.New(t)
old := newRedisClient
defer func() { newRedisClient = old }()
newRedisClient = func(options *redis.Options) *redis.Client {
assert.Equal("127.0.0.1:1001", options.Addr)
assert.Equal("password", options.Password)
assert.Equal(1, options.DB)
statusCmdMock := new(redisStatusCmdMock)
statusCmdMock.On("Result").Return("success", nil)
clientMock := new(redisClientMock)
clientMock.On("Ping").Return(statusCmdMock)
return clientMock
}
options := Options{}
options.Addr = "127.0.0.1:1001"
options.Password = "password"
options.DB = 1
r := MyRedis{options: options}
result, err := r.Connect()
assert.Equal("success", result)
assert.Equal(nil, err)
}
I get the following error: cannot use clientMock (type *redisClientMock) as type *redis.Client in return argument. I think I read that I need to mock all the functions of redis.Client in order to be able to use it as a mock in this case, but is that really the case? That seems like it's overkill and I should be able to do this in some way. How do I go about getting this test to work, or do I need to restructure my code so that it's easier to write the test?
redis.Client is a struct type and in Go struct types are simply not mockable. However interfaces in Go are mockable, so what you can do is to define your own "newredisclient" func that instead of returning a struct returns an interface. And since interfaces in Go are satisfied implicitly you can define your interface such that it will be implemented by redis.Client out of the box.
type RedisClient interface {
Ping() redis.StatusCmd
// include any other methods that you need to use from redis
}
func NewRedisCliennt(options *redis.Options) RedisClient {
return redis.NewClient(options)
}
var newRedisClient = NewRedisClient
If you also want to mock the return value from Ping(), you need to do a bit more work.
// First define an interface that will replace the concrete redis.StatusCmd.
type RedisStatusCmd interface {
Result() (string, error)
// include any other methods that you need to use from redis.StatusCmd
}
// Have the client interface return the new RedisStatusCmd interface
// instead of the concrete redis.StatusCmd type.
type RedisClient interface {
Ping() RedisStatusCmd
// include any other methods that you need to use from redis.Client
}
Now *redis.Client does not satisfy the RedisClient interface anymore because the return type of Ping() is different. Note that it doesn't matter that the result type of redis.Client.Ping() satisfies the interface type returned by RedisClient.Ping(), what matters is that the method signatures are different and therefore their types are different.
To fix this you can define a thin wrapper that uses *redis.Client directly and also satisfies the new RedisClient interface.
type redisclient struct {
rc *redis.Client
}
func (c *redisclient) Ping() RedisStatusCmd {
return c.rc.Ping()
}
func NewRedisCliennt(options *redis.Options) RedisClient {
// here wrap the *redis.Client into *redisclient
return &redisclient{redis.NewClient(options)}
}
var newRedisClient = NewRedisClient

Can golang plugins be used for factory functions?

I have the following code in a golang plugin module:
plug.go
package main
import "fmt"
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) thing {
return New(s)
}
it is compiled as a .so object and used in another program:
main.go
package main
import (
"fmt"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory") // <-problems start here
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type Sayer interface {
Say() string
}
type GetSayer interface {
Make(string) Sayer
}
I'm able to lookup the Thing, and call Say() on it, but the second interface conversion panics:
first thing - 1
panic: interface conversion: *main.thingFactory is not main.GetSayer: missing method Make
even though the runtime recognizes the first symbol as a Sayer it doesn't recognize that thingFactory obviously has a Make() method, which should return something that is also a Sayer.
Am I missing something obvious here?
The first problem is that in your plugin thingFactory (more precicely *thingfactory) does not have a method described in your main app's GetSayer interface:
Make(string) Sayer
You have:
Make(string) thing
So (first) you have to change thingFactory.Make() to this:
type Sayer interface {
Say() string
}
func (t thingFactory) Make(s string) Sayer {
th := New(s)
return &th
}
After this it still won't work. And the reason for this is because the plugin's Sayer type is not identical to your main app's Sayer type. But they must be the same in order to implement your main app's GetSayer interface.
One solution is to "outsource" the Sayer interface to its own package, and use this common, shared package both in the plugin and in the main app.
Let's create a new package, call it subplay:
package subplay
type Sayer interface {
Say() string
}
Import this package and use it in the plugin:
package main
import (
"fmt"
"path/to/subplay"
)
var (
Thing = New("first thing")
ThingFactory = thingFactory{}
)
type thing struct {
i int
s string
}
func New(s string) thing {
return thing{s: s}
}
func (t *thing) Say() string {
t.i++
return fmt.Sprintf("%s - %d", t.s, t.i)
}
type thingFactory struct{}
func (t thingFactory) Make(s string) subplay.Sayer {
th := New(s)
return &th
}
And also import and use it in the main app:
package main
import (
"fmt"
"path/to/subplay"
"plugin"
)
func main() {
p, err := plugin.Open("../plug/plug.so")
if err != nil {
panic(err)
}
symbol, err := p.Lookup("Thing")
if err != nil {
panic(err)
}
thing := symbol.(subplay.Sayer)
fmt.Println(thing.Say())
symbol, err = p.Lookup("ThingFactory")
if err != nil {
panic(err)
}
factory := symbol.(GetSayer)
madeThing := factory.Make("how about me?")
fmt.Println(madeThing.Say())
fmt.Println(madeThing.Say())
}
type GetSayer interface {
Make(string) subplay.Sayer
}
Now it will work, and output will be:
first thing - 1
how about me? - 1
how about me? - 2
See related questions:
go 1.8 plugin use custom interface
How do Go plugin dependencies work?
Your plugin Make method should return a Sayer object not thing
type Sayer interface {
Say() string
}
func (t *thingFactory) Make(s string) Sayer {
return New(s)
}

Extend struct in Go

Suppose object A has a field of type net.Dialer. I'd like to provide object A with a custom implementation of net.Dialer that augments the Dial method. Is this doable in Go? I'm trying to use embedded fields like so:
package main
import (
"net"
"fmt"
)
type dialerConsumer struct {
dialer net.Dialer
}
func (dc *dialerConsumer) use() error {
conn, e := dc.dialer.Dial("tcp", "golang.org:http")
if e != nil {
return e
}
fmt.Printf("conn: %s\n", conn)
return nil
}
type customDialer struct {
net.Dialer
}
func main() {
standardDialer := net.Dialer{}
consumer := &dialerConsumer{
dialer: standardDialer,
}
consumer.use()
/*
customDialer := customDialer{
net.Dialer{},
}
consumer = &dialerConsumer{
dialer: customDialer,
}
consumer.use()
*/
}
However, when I uncomment the commented-out code in main, I get the following compilation error:
src/test.go:38: cannot use customDialer (type customDialer) as type net.Dialer in field value
You're getting the error because customDialer and net.Dialer are two different types and cannot be used interchangeably. Embedding in Go is not the same as class inheritance in other OO langauges so it won't help you with what you trying to do.
What you can do instead in this case is to use Go interfaces which give you something like polymorphism/duck-typing, and since interfaces in Go are satified implicitly you can define a new interface that an existing type will implement by virtue of having a method with the same signature as the newly defined interface.
// already implemented by net.Dialer
type Dialer interface {
Dial(network, address string) (net.Conn, error)
}
type customDialer struct {
*net.Dialer
}
func (cd *customDialer) Dial(network, address string) (net.Conn, error) {
conn, err := cd.Dialer.Dial(network, address)
if err != nil {
return nil, err
}
fmt.Printf("conn: %s\n", conn)
return conn, nil
}
// now the dialer field can be set to *customDialer and net.Dialer as well
type dialerConsumer struct {
dialer Dialer
}
https://play.golang.org/p/i3Vpsh3wii

Resources