Creating a map of constructor functions - go

So I'd like to have a map of names of functions, to choose an implementation of an interface based on an environment variable. I've reproduced it in the following code:
package test
type Fooer interface {
Foo() error
}
type Bar struct{}
func NewBar() (*Bar, error) { return &Bar{}, nil }
func (b *Bar) Foo() error { return nil }
type Baz struct{}
func NewBaz() (*Baz, error) { return &Baz{}, nil }
func (b *Baz) Foo() error { return nil }
var constructors map[string]func() (*Fooer, error)
func init() {
constructors = map[string]func() (*Fooer, error){
"bar": NewBar,
"baz": NewBaz,
}
}
This throws the following errors when I go build test.go:
./test.go:21: cannot use NewBar (type func() (*Bar, error)) as type func() (*Fooer, error) in map value
./test.go:22: cannot use NewBaz (type func() (*Baz, error)) as type func() (*Fooer, error) in map value
So what am I doing wrong? Can I use a *Fooer as the return type of a constructor function? What would be the actual best way to approach this? (I'm new to Go)

Do not (almost never) pass around pointers to interface. You will (almost) never need a *Fooer, a Fooer is enough.
Yes, your Bar and Baz constructors could return a Fooer (not a *Fooer!) but that seems awkward.
Keeping the constructors in a map and querying the map for a constructor seem like one level of indirection too much. Are you constantly creating new Bar and Baz?

Related

Testing my interface in golang with mocks. Specifically test 1 function that calls a sibling function

Let's say I have this code and I want to create a test for Foo()
The important part it Foo makes a call to Bar
package main
type MyInterface interface {
Foo() error
Bar() error
}
type MyStruct struct {
}
func NewMyStruct() MyInterface{
return &MyStruct{}
}
func (m *MyStruct) Foo() error {
// do something
m.Bar()
// do something else
return nil
}
func (m *MyStruct) Bar() error {
// do something
return nil
}
Is it possible to create a test for this where I can mock the behaviour of Bar before I run Foo? or am I doing something fundamentally wrong?
I can see that if I extracted Bar into its own service I would mock it that way but that also doesn't feel right.
Any insights or links to documentation would be great.
You should be able to achieve what you need with a couple of changes. First, let me present the code and then I'll walk you through all of the relevant changes.
main.go file
package main
type MyInterface interface {
Foo() error
Bar() error
}
type MyStruct struct {
DS MyInterface
}
// here you've to depend upon an interface and return a pointer to a struct
func NewMyStruct(ds MyInterface) *MyStruct {
return &MyStruct{
DS: ds,
}
}
func (m *MyStruct) Foo() error {
// do something
m.DS.Bar()
// do something else
return nil
}
func (m *MyStruct) Bar() error {
// do something
return nil
}
func main() {}
Here, I changed a couple of things in order to be able to successfully mock our dependencies. Let me recap them:
The MyStruct has a dependency of type MyInterface
The function NewMyStruct accepts the interface as parameter and returns a pointer to the MyStruct struct
In the Foo method, we're going to rely on the DS field of our pointer receiver type instance
Now, let's switch to the test file.
main_test.go file
package main
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
// 1. declare mock
type myStructMock struct {
mock.Mock
}
// 2. implement the interface
func (m *myStructMock) Foo() error {
args := m.Called()
return args.Error(0)
}
func (m *myStructMock) Bar() error {
args := m.Called()
return args.Error(0)
}
func TestFoo(t *testing.T) {
// 3. instantiate/setup mock
myStructMock := new(myStructMock)
myStructMock.On("Bar").Return(nil).Times(1)
sut := NewMyStruct(myStructMock)
err := sut.Foo()
// 4. check that all expectations were met on the mock
assert.Nil(t, err)
assert.True(t, myStructMock.AssertExpectations(t))
}
Here, I found it best to add comments within the code to give you a chronological order of what's going on. The idea is that the real system tested (e.g. the sut variable) is relying on mock instead of actual implementations. Thanks to the method Times, we make sure that the Bar method is called once.
I always used this approach when it comes to testing production code and I find it pretty flexible and amazing!
Let me know if this helps you or if you still need something else, thanks!

Define type and method local in a func?

My code uses the interface with one function:
func InsertRow(rec []string) error
There are different types with different implementation of this interface. Now I would like to test this using "go test". In this case the implementation of InsertRow should do nothing:
func (t TestInserter) InsertRow(rec []string) error {
return nil
}
I can define a type internal in a test function. But now I would also like to define a dummy method for this type:
func TestInserter01(t *testing.T) {
type TestMyInserter struct {} <-- Test type
func (t TestMyInserter) InsertRow(rec []string) error { <-- Dummy function on this type.
return nil
}
... using InsertRow as a parameter in another function ...
}
But this produces compile errors:
expected operand, found ']'
expected ']', found 'return'
The same code works, if I define both the type and the method outside the test function.
Is it possible to hide the test implementation in the test function and do not define it outside the function? I need many of them, so that I would prefer to have them defined locally in the test function.
No, it's not possible. Method declarations may only be at the top level (outside of any function).
Spec: Declarations and scope:
Declaration = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .
See related: Is it possible to define an anonymous interface implementation in Go?
Note howerer that it's possible to supply "dynamic" implementations with a helper type. Meaning you'll provide the method implementations inside a function, and with the help of a helper type that implements the interface, you can get a "dynamic" implementation.
For example:
type Inserter interface {
InsertRow(rec []string) error
}
type helper func(rec []string) error
func (h helper) InsertRow(rec []string) error {
return h(rec)
}
func main() {
testInsert := func(rec []string) error {
return fmt.Errorf("rec: %v", rec)
}
var i Inserter = helper(testInsert)
err := i.InsertRow([]string{"one", "two"})
fmt.Println(err)
}
This will output (try it on the Go Playground):
rec: [one two]
A variant may be a struct holding fields of function types for the methods. It may be used to cover multiple methods:
type helper struct {
insertRow func(rec []string) error
}
func (h helper) InsertRow(rec []string) error {
return h.insertRow(rec)
}
func main() {
h := helper{
insertRow: func(rec []string) error {
return fmt.Errorf("rec: %v", rec)
},
}
var i Inserter = h
err := i.InsertRow([]string{"one", "two"})
fmt.Println(err)
}
This outputs the same. Try it on the Go Playground.

assinging functions to function types that return interface values,

Im trying to define a couple of interface to refactor some code and I'm getting a problem where Go is not letting me assign functions to variables.
This is the set up
main.go
type Gettable interface {
Get() int64
}
type MyFunction func(int64) (Gettable, error)
func main() {
var f MyFunction
f = sub.TestFn2
a, _ := f(1)
fmt.Println(a)
}
main/sub
package sub
type MyStruct struct {
Val int64
}
func (v MyStruct) Get() int64 {
return v.Val
}
func TestFn2(a int64) (MyStruct, error) {
return MyStruct{a}, nil
}
I'm trying to define a generic function type, and in the sub package create the concrete functions
and ideally i want to store the functions in a map and call them with something like
FnMap["fnName"]()
I'm not there yet,
im getting an error saying
/topics.go:27:4: cannot use sub.TestFn2 (type func(int64) (sub.MyStruct, error)) as type MyFunction in assignment
but MyStruct clearly implements the interface Gettable
This error occurs because of signature don't match.
You code shared is:
//shared code
//---------------------------------------------
type Gettable interface {
Get() int64
}
type MyFunction func(int64) (Gettable, error)
So you need replace MyStruct by Gettable.
//main/sub
//---------------------------------------------
type MyStruct struct {
Val int64
}
func (v MyStruct) Get() int64 {
return v.Val
}
//this signature of TestFn2 is different of MyStruct
//-[TestFn2] func (a int64) (MyStruct, error)
//-[MyFunction] func(int64) (Gettable, error)
func TestFn2(a int64) (Gettable, error) {//<-- replace by Gettable here
return MyStruct{a}, nil
}
Running your code:
//main.go
//---------------------------------------------
func main() {
var f MyFunction
f = TestFn2
a, _ := f(1)
fmt.Println(a)
}
Result is:
{1}
See in playground: https://play.golang.org/p/sRsXix8E_83
As per the Go's assignability rules a function f is assignable to a variable v only if the variable's type T exactly matches the f's signature.
The ability to assign a more specific type in some other languages called "covariance", and Go's type system does not have it.

Why does method signature have to perfectly match interface method

I began to learn Go and have trouble understanding the following:
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i int) {
fmt.Println(i)
}
func main() {
var i I = S{}
i.foo(2)
}
This fails with:
cannot use S literal (type S) as type I in assignment:
S does not implement I (wrong type for foo method)
have foo(int)
want foo(interface {})
I don't understand why Go doesn't accept the foo(int) signature given the fact that int implements interface {}. Can anyone help with an explanation?
I think your understanding of interface isn't sound. Interface{} itself is a type. It consists of two things : underlying type and underlying value.
Golang doesn't have overloading. Golang type system matches by name and requires consistency in the types
So, when you are defining a function taking a interface type as a parameter:
foo(interface {})
This is a different function from a function taking int type:
foo(int)
So you should change the following line to
func (s S) foo(i interface{}) {
fmt.Println(i)
}
Or better yet to this:
type I interface {
foo()
}
type S struct {
I int
}
func (s S) foo() {
fmt.Println(s.I)
}
func main() {
var i I = S{2}
i.foo()
}
The error message itself is self-explaining:
"S does not implement I (wrong type for foo method)"
so S{} which is of S type cannot be used on the RHS of type I variable assignment.
To implement I interface, type S needs to define foo function with the exact same signature.
To achieve what you wanted, you can use empty interface as input parameter for your foo function in S and do type assertion
package main
import "fmt"
type I interface {
foo(interface {})
}
type S struct {}
func (s S) foo(i interface{}) {
if i, ok := i.(int); ok{
fmt.Println(i)
}
}
func main() {
var i I = S{}
i.foo(2)
i.foo("2")
}
Try it in go playground

Accept function in argument with empty interface return type

I would like to understand why the code snippet below does not compile. What is the Go way of accepting a function as a function argument that may have any return type?
package main
func main() {
test(a) // Error: cannot use a (type func() string) as type func() interface {} in argument to test
test(b) // Error: cannot use b (type func() int) as type func() interface {} in argument to test
}
func a() string {
return "hello"
}
func b() int {
return 1
}
func test(x func() interface{}) {
// some code...
v := x()
// some more code....
}
Play: https://play.golang.org/p/CqbuEZGy12
My solution based on Volker's answer:
package main
import (
"fmt"
)
func main() {
// Wrap function a and b with an anonymous function
// that has an empty interface return type. With this
// anonymous function, the call signature of test
// can be satisfied without needing to modify the return
// type of function a and b.
test(func() interface{} {
return a()
})
test(func() interface{} {
return b()
})
}
func a() string {
return "hello"
}
func b() int {
return 1
}
func test(x func() interface{}) {
v := x()
fmt.Println(v)
}
Play: https://play.golang.org/p/waOGBZZwN7
You tripped over a very common misconception for Go newcomers: The empty interface interface{} does not mean "any type". Really, it does not. Go is statically typed. The empty interface interface {} is an actual (strongly typed type) like e.g. string or struct{Foo int} or interface{Explode() bool}.
That means if something has the type interface{} it has that type and not "any type".
Your function
func test(x func() interface{})
takes one parameter. This parameter is a (parameterless function) which returns a specific type, the type interface{}. You can pass any function to test which matches this signature: "No parameters and return interface{}". None of your functions a and b match this signature.
As said above: interface {} is not a magical abbreviation for "whatever",it is a distinct static type.
You have to change e.g. a to:
func a() interface{} {
return "hello"
}
Now this might look strange as you return a string which is not of type interface{}. This works because any type is assignable to variables of type interface{} (as every type has at least no methods :-).
As the Go specification states:
A function type denotes the set of all functions with the same parameter and result types
In your case, your result types differ (string vs interface{})
To be able to receive a function with any kind of result type, test would have to be defined as:
func text(x interface{}) { ... }
and then you will have to use reflect package to call the function stored in x.
Edit
Such a test function would look like this:
func test(x interface{}) {
v := reflect.ValueOf(x)
if v.Kind() != reflect.Func {
panic("Test requires a function")
}
t := v.Type()
if t.NumIn() != 0 && t.NumOut() != 1 {
panic("Function type must have no input parameters and a single return value")
}
values := v.Call(nil)
val := values[0].Interface()
// some more code..
}
Playground: https://play.golang.org/p/trC2lOSLNE

Resources