2

I understand that programs in Go are run from the main function as their starting point. But I was wondering what the workflow is for creating functions for a new package.

In python for example I use __main__ in module's when the module is called directly. When the module is imported from another file __main__ would be ignored. Which is useful when you're developing a new module.

if __name__ == "__main__":
    # Run module code here if module called directly

For Go I'm using a test.go file with a package main along with my main.go file to test functions in packages I'm creating.

// test.go
package main

import (
    "newpackage"
)

func main() {
    newpackage.MyNewFunc()
}

Is there a better way to do this or is this the standard workflow? Thanks.

AppTest
  • 491
  • 1
  • 7
  • 23
  • 1
    Yes, there is: Please read https://golang.org/cmd/go/#hdr-Test_packages and https://golang.org/pkg/testing/ – Volker Jun 08 '17 at 06:35
  • It looks weird to import main package from other packages. Can you provide some details to tell us why you have such a scenario? – Heissenberger Jun 08 '17 at 07:44
  • I'm not sure what you mean. I'm not trying to import the main package. I was just wondering if there was an equivalent to python's `if __name__ == "__main__":` which lets me try individual functions in a module without having to write a test file. See https://stackoverflow.com/a/4041260/5319281 – AppTest Jun 08 '17 at 08:11
  • But as others have pointed out Go's testing tools seem the best way to go. – AppTest Jun 08 '17 at 08:11

1 Answers1

9

You don't test in Go using main. Go has its own test framework.

First, read "How to Write Go Code" which will explain Go's package layout and testing tools. It's best to go with them because so much of the Go tools expect that layout.

When creating a package, put it somewhere in ~/go/src. I'd recommend following the convention using the repository you like to use, even for things you aren't necessarily going to upload. It makes for better organization; go get will put other external packages in ~/go/src/ as well.

For example, I'd use ~/go/src/github.com/schwern/newpackage/ even though I don't intend to upload this to Github. github.com/schwern acts as my "organization" within the Go source tree.

Put the functions into newpackage.go under package newpackage.

$ cat ~/go/src/github.com/schwern/newpackage/newpackage.go 
package newpackage

func MyNewFunc() string {
    return "Hello!"
}

Then tests go in newpackage_test.go right next to newpackage.go. These should be familiar from Python, write a bunch of Test* functions. Unlike Python it doesn't use asserts.

$ cat ~/go/src/github.com/schwern/newpackage/newpackage_test.go 
package newpackage_test

import(
    "testing"
    "github.com/schwern/newpackage"
)

func TestMyNewFunc( t *testing.T ) {
    want := "Hello!"
    have := newpackage.MyNewFunc()

    if have != want {
        t.Errorf("MyNewFunc(): have: '%v', want: '%v'", have, want )
    }
}

If you run go test inside the package directory it will compile the current package and its dependencies, find and compile all *_test.go files in the package directory, and execute their Test* functions.

$ pwd
/Users/schwern/go/src/github.com/schwern/newpackage
$ go test -v
=== RUN   TestMyNewFunc
--- PASS: TestMyNewFunc (0.00s)
PASS
ok      github.com/schwern/newpackage   0.013s

Note that the test is in a different package than what its testing. That makes it a blackbox test, it can only see the exported (ie. UpperCase) functions. You can make a glassbox test by putting the tests in the same package, it's best to do that in a separate file like newpackage_internal_test.go.

Unfortunately Go doesn't come with assert functions, the above if and call to t.Errorf is the equivalent. Rather than constantly hand-roll them, there's libraries out there which provide assert functions like stvp/assert. After running go get github.com/stvp/assert you could write...

package newpackage_test

import(
    "testing"
    "github.com/schwern/newpackage"
    "github.com/stvp/assert"
)

func TestMyNewFunc( t *testing.T ) {
    assert.Equal( t, newpackage.MyNewFunc(), "Hello!" )
}

If you want an executable that uses newpackage, it should probably go in its own package. Unless it's an integral part of newpackage.

$ cat ~/go/src/github.com/schwern/newexec/main.go 
package main

import (
    "fmt"
    "github.com/schwern/newpackage"
)

func main() {
    fmt.Println(newpackage.MyNewFunc())
}

If you want to test main, the testing package provides a special TestMain function... though I admit I do not fully understand it. Like any other language, it's best to put as much functionality as possible into library calls and have main be a thin wrapper.

Schwern
  • 153,029
  • 25
  • 195
  • 336