Issue implementing interface method that returns another interface

138 views
Skip to first unread message

Rick Schubert

unread,
Feb 28, 2023, 12:08:06 PM2/28/23
to golang-nuts
I am having a question about how to implement interfaces correctly in Go when it comes to third-party packages that use chained methods. I have compiled an example project below for you so that you can understand the problem.


package main

import (
myAPI "github.com/hashicorp/vault/api"
)

var myClient *myAPI.Client

type MyProvider interface {
GetClient() MyAPIClient
}

type MyAPIClient interface {
// I want to do this but it does not work
Logical() MyAPILogical
// This works though
// Logical() *myAPI.Logical
}

type MyAPILogical interface {
Write(path string, data map[string]interface{}) (*myAPI.Secret, error)
}

type Provider struct {}

func PublicFunctionIWantToTest(provider MyProvider) {
client := provider.GetClient()
// We normally do something here with the 'client' variable, but important
// is that we forward it later on
privateFunctionThatIsUsedInTheTest(client)
}

func privateFunctionThatIsUsedInTheTest(client MyAPIClient) (*myAPI.Secret, error) {
return client.Logical().Write(
"/here/goes/some/path",
map[string]interface{}{
"key": "value",
},
)
}

func NewProvider() MyProvider {
return Provider{}
}

func (p Provider) GetClient() MyAPIClient {
return myClient
}

// Empty function just so that this example can be built
func main() {}



As you can see, the package has a chained method Logical().Write() . Since I want to create tests for PublicFunctionIWantToTest, I want to pass down all the functionality as interface so that I can use https://vektra.github.io/mockery/ to create mocks for it.

Unfortunately, I am hitting an issue with my MyAPIClient and the MyAPILogical interface. Since I can see in the package's documentation (https://pkg.go.dev/github.com/hashicorp/vault/a...@v1.8.1#Logical.Write) that the Logical() method returns a Logical instance, I want to make it so that interface method returns the other interface MyAPILogical (line 15). This does not work though, there is an error on line 47 in the GetClient() method saying that I would not implement the interface correctly. How could I do that?

cannot use myClient (variable of type *api.Client) as MyAPIClient value in return statement: *api.Client does not implement MyAPIClient (wrong type for method Logical)
have Logical() *api.Logical
want Logical() MyAPILogicalcompilerInvalidIfaceAssign


Thank you kindly

Axel Wagner

unread,
Feb 28, 2023, 1:20:54 PM2/28/23
to Rick Schubert, golang-nuts
You are running into this: https://go.dev/doc/faq#covariant_types

> How could I do that?

You probably have to write a wrapper. Or change the concrete type to return an interface. Or test using the concrete type, instead of using mocks.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/1bf2aef5-14af-4565-8f4c-538a1edeb380n%40googlegroups.com.

Rick Schubert

unread,
Mar 2, 2023, 11:22:56 AM3/2/23
to golang-nuts
Thank you for the help @Axel Wagner
Reply all
Reply to author
Forward
0 new messages