Search code examples
gotestingtypesinterfacelocal

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.


Solution

  • 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.