创建Go语言包的工作流程

huangapple go评论76阅读模式
英文:

Workflow for creating packages in Go

问题

我理解了,Go语言中的程序是从main函数作为起始点运行的。但我想知道创建新包的函数的工作流程是怎样的。

例如,在Python中,当模块直接被调用时,我会在模块中使用__main__。当模块从另一个文件中导入时,__main__会被忽略。这在开发新模块时非常有用。

if __name__ == "__main__":
    # 在此处运行模块代码,如果模块直接被调用

对于Go语言,我使用一个test.go文件和一个带有package mainmain.go文件来测试我创建的包中的函数。

// test.go
package main

import (
    "newpackage"
)

func main() {
    newpackage.MyNewFunc()
}

是否有更好的方法来做这个,或者这是标准的工作流程?谢谢。

英文:

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.

答案1

得分: 9

在Go中,你不使用main来进行测试。Go有自己的测试框架。

首先,阅读《如何编写Go代码》(https://golang.org/doc/code.html),它将解释Go的包布局和测试工具。最好按照它们的方式进行,因为Go工具的很多功能都期望使用这种布局。

创建一个包时,将其放在~/go/src的某个位置。我建议按照你喜欢使用的存储库的约定进行,即使对于你不打算上传的内容也是如此。这样可以更好地组织代码;go get也会将其他外部包放在~/go/src/中。

例如,我会使用~/go/src/github.com/schwern/newpackage/,即使我不打算将其上传到GitHub。github.com/schwern在Go源代码树中充当我的“组织”。

将函数放在newpackage.go中,其位于package newpackage下面。

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

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

然后,测试代码放在newpackage_test.go中,与newpackage.go放在一起。这些测试代码应该与Python中的写法相似,编写一系列的Test*函数。与Python不同的是,Go测试框架不使用断言。

$ 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)
    }
}

如果你在包目录中运行go test,它将编译当前包及其依赖项,找到并编译包目录中的所有*_test.go文件,并执行它们的Test*函数。

$ 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

请注意,测试代码位于与其进行测试的代码不同的包中。这使得它成为一个黑盒测试,它只能看到导出的(即大写字母开头的)函数。你可以通过将测试代码放在同一个包中来进行白盒测试,最好在一个单独的文件中,比如newpackage_internal_test.go

不幸的是,Go没有内置的断言函数,上述的if语句和调用t.Errorf是等效的。为了避免反复手动编写断言,有一些库提供了断言函数,比如stvp/assert。在运行go get github.com/stvp/assert之后,你可以这样编写代码...

package newpackage_test

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

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

如果你想要一个使用newpackage的可执行文件,它应该放在自己的包中,除非它是newpackage的一个组成部分。

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

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

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

如果你想要测试main函数,testing包提供了一个特殊的TestMain函数...虽然我承认我并不完全理解它。与任何其他语言一样,最好将尽可能多的功能放入库调用中,并让main函数成为一个轻量级的包装器。

英文:

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.

huangapple
  • 本文由 发表于 2017年6月8日 13:49:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/44427587.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定