Hi I'm trying to retrieve the function/method of one struct but I'm using an interface as parameter and using this interface I'm trying to access the function of the struct. To demonstrate what I want below is my code
// Here I'm trying to use "GetValue" a function of RedisConnection but since "c" is an interface it doesn't know that I'm trying to access the RedisConnection function. How Do I fix this?
func GetRedisValue(c Connection, key string) (string, error) {
value, err := c.GetValue(key)
return value, err
}
// Connection ...
type Connection interface {
GetClient() (*redis.Client, error)
}
// RedisConnection ...
type RedisConnection struct {}
// NewRedisConnection ...
func NewRedisConnection() Connection {
return RedisConnection{}
}
// GetClient ...
func (r RedisConnection) GetClient() (*redis.Client, error) {
redisHost := "localhost"
redisPort := "6379"
if os.Getenv("REDIS_HOST") != "" {
redisHost = os.Getenv("REDIS_HOST")
}
if os.Getenv("REDIS_PORT") != "" {
redisPort = os.Getenv("REDIS_PORT")
}
client := redis.NewClient(&redis.Options{
Addr: redisHost + ":" + redisPort,
Password: "", // no password set
DB: 0, // use default DB
})
return client, nil
}
// GetValue ...
func (r RedisConnection) GetValue(key string) (string, error) {
client, e := r.GetClient()
result, err := client.Ping().Result()
return result, nil
}
To answer the question directly, i.e., to cast an interface into a concrete type, you do:
v = i.(T)
where i is the interface and T is the concrete type. It will panic if the underlying type is not T. To have a safe cast, you use:
v, ok = i.(T)
and if the underlying type is not T, ok is set to false, otherwise true. Note that T can also be an interface type and if it is, the code cast i into a new interface instead of a concrete type.
And please be noted, casting an interface is likely a symbol of bad design. As in your code, you should ask yourself, does your custom interface Connection solely requires GetClient or does it always requires a GetValue? Does your GetRedisValue function requires a Connection or does it always wants a concrete struct?
Change your code accordingly.
Your Connection interface:
type Connection interface {
GetClient() (*redis.Client, error)
}
only says that there is a GetClient method, it says nothing about supporting GetValue.
If you want to call GetValue on a Connection like this:
func GetRedisValue(c Connection, key string) (string, error) {
value, err := c.GetValue(key)
return value, err
}
then you should include GetValue in the interface:
type Connection interface {
GetClient() (*redis.Client, error)
GetValue(string) (string, error) // <-------------------
}
Now you're saying that all Connections will support the GetValue method that you want to use.
Related
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
I'm trying to create a layer on top of a third party library, in this case libchan. Here's an interface I've defined:
type ReceiverStream interface {
Receive(msg interface{}) error
}
type InboundTransport interface {
WaitReceiveChannel() (ReceiverStream, error)
}
The InboundTransport is meant to be a stand-in for a type Transport:
// libchan.go
type Transport interface {
// NewSendChannel creates and returns a new send channel. The receive
// end will get picked up on the remote end of the transport through
// the remote calling WaitReceiveChannel.
NewSendChannel() (Sender, error)
// WaitReceiveChannel waits for a new channel be created by the
// remote end of the transport calling NewSendChannel.
WaitReceiveChannel() (Receiver, error)
}
Just for context, this is the libchan.Receiver definition (please note that it matches my ReceiverStream:
// libchan.go
type Receiver interface {
// Receive receives a message sent across the channel from
// a sender on the other side of the underlying transport.
// Receive is expected to receive the same object that was
// sent by the Sender, any differences between the
// receive and send type should be handled carefully. It is
// up to the application to determine type compatibility, if
// the receive object is incompatible, Receiver will
// throw an error.
Receive(message interface{}) error
}
The Transport is returned by the libchan library here:
// libchan/session.go:62
func NewTransport(provider StreamProvider) libchan.Transport {
...
}
Since libchan.Transport and InboundTransport share a WaitReceiveChannel() (ReceiverStream, error) method, I figured I should be able to sub one for the other, like so:
func (ln SpdyListener) Accept(addr string) InboundTransport {
var listener net.Listener
var err error
listener, err = net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
c, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
p, err := spdy.NewSpdyStreamProvider(c, true)
if err != nil {
log.Fatal(err)
}
return spdy.NewTransport(p)
}
But I get an error:
cannot use spdy.NewTransport(p) (type libchan.Transport) as type InboundTransport in return argument:
libchan.Transport does not implement InboundTransport (wrong type for WaitReceiveChannel method)
have WaitReceiveChannel() (libchan.Receiver, error)
want WaitReceiveChannel() (ReceiverStream, error)
I assume that what this error means is that a type of ReceiverStream does not match libchan.Receiver, but I thought that golang interfaces were implicit, meaning that as long as the return type implements the same methods as the expected interface, it would pass compilation. Is there anything I can change so that I can superimpose a self-defined interface onto one returned by a third part library?
TLDR: A third party lib is returning an object of interface Transport. The Transport interface specifies a method WaitReceiveChannel(). I have a self-defined interface InboundTransport that also specifies WaitReceiveChannel(). The third-party method I'm calling returns an object that implements Transport by way of method WaitReceiveChannel(). I assumed that it would also implement InboundTransport since the latter also specifies a WaitReceiveChannel() of the same type. This isn't working. Why not?
As you already know interfaces in Go are satisfied implicitly.
But, as the error states,
WaitReceiveChannel() (libchan.Receiver, error)
and
WaitReceiveChannel() (ReceiverStream, error)
are two different method types, resulting in libchan.Transport not implicitly implementing InboundTransport.
To work around this you have to write a thin wrapper around libchan.Transport that implements the InboundTransport properly.
type TransportWrapper struct {
t *libchan.Transport
}
func (w *TransportWrapper) WaitReceiveChannel() (Receiver, error) {
return w.t.WaitReceiveChannel()
}
// ...
func (ln SpdyListener) Accept(addr string) InboundTransport {
var listener net.Listener
var err error
listener, err = net.Listen("tcp", addr)
if err != nil {
log.Fatal(err)
}
c, err := listener.Accept()
if err != nil {
log.Fatal(err)
}
p, err := spdy.NewSpdyStreamProvider(c, true)
if err != nil {
log.Fatal(err)
}
return &TransportWrapper{spdy.NewTransport(p)}
}
Can somebody explain how can this happened?
I put interface as parameter in a function. While invoking this function I pass struct into it, but it didn't give me error. Here's the code
package main
import (
"fmt"
"github.com/myusername/gomodel/domain"
"github.com/myusername/gomodel/model"
)
func main() {
db := model.InitDB()
newFunc(db)
}
func newFunc(db domain.IUser) {
r, err := db.CreateUserTable()
if err != nil {
fmt.Println("error", err)
}
fmt.Println(r)
}
I've implemented the interface somewhere else in the code, because the program just work as the implemented interface expected to be.
IUser is an interface whose member is:
type IUser interface {
CreateUserTable() (sql.Result, error)
}
InitDB is a function to open the database and return struct of database:
type DB struct {
*sql.DB
}
//InitDB initializes the database
func InitDB() *DB {
db, err := sql.Open(dbDriver, dbName)
if err != nil {
log.Fatal("failed to initialize database: ",err)
}
err2 := db.Ping()
if err2 != nil {
log.Fatal(err2)
}
return &DB{db}
}
My question is: how can a function with a parameter type interface be passed a different type of parameter? And how is this working under the hood?
As per Golang Spec
An interface type specifies a method set called its interface. A
variable of interface type can store a value of any type with a method
set that is any superset of the interface. Such a type is said to
implement the interface.
This is because interface can be implemented as a wrapper to every type. Interface actually points to two things mainly one is the underlying type which is a struct here and other one is the value of that type which is a pointer to DB
You see newFunc is actually taking interface{} as an argument, So you can pass anything to it of type T which can be of primitive types too.
func main() {
db := model.InitDB()
newFunc(db)
}
So In case you want to get the underlying value you need to type assert. Interface works like a wrapper to the struct here and save its type and value which can be get using type assertion.
Is it possible to have my function definition below accept any type of struct?
I've tried to refactor like so:
// This method should accept any type of struct
// Once I receive my response from the database,
// I scan the rows to create a slice of type struct.
func generateResponse(rows *sqlx.Rows, structSlice []struct{}, structBody struct{}) ([]struct{}, error) {
for rows.Next() {
err := rows.StructScan(&structBody)
if err != nil {
return nil, err
}
structSlice = append(structSlice, structBody)
}
err := rows.Err()
if err != nil {
return nil, err
}
return structSlice, nil
}
Assume my struct is of type OrderRevenue.
When I call the function above:
structSlice, err := generateResponse(rows, []OrderRevenue{}, OrderRevenue{})
The error I get is:
cannot use []OrderRevenue literal as type []struct{} in argument...
Am I going about this the wrong way?
This is considered the cornerstone (or more of a limitation) of Go's type system. struct{} is an unnamed type that is different from struct{ field1 int } and of course is not the same as OrderRevenue{}.
Go emphasizes abstraction through interfaces, and perhaps you should try that. Here is the first take:
type OrderRevenue interface {
MarshalMyself() ([]byte, error)
}
type Anonymous struct {}
func (a Anonymous) MarshalMyself() ([]byte, error) {
// implementation's up to you
return []byte{}, nil
}
// the function signature
generateResponse(rows *sqlx.Rows, structSlice []OrderRevenue, structBody Body) ([]Body, error) {
// ...
}
In this case you can also use empty interface interface{}, which all types implement, but you'll have to recursively go through the structure to do manual type assertion. The best approach in Go is to know the shape of your data in advance, at least partially.
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