英文:
Workflow for creating packages in Go
问题
我理解了,Go语言中的程序是从main
函数作为起始点运行的。但我想知道创建新包的函数的工作流程是怎样的。
例如,在Python中,当模块直接被调用时,我会在模块中使用__main__
。当模块从另一个文件中导入时,__main__
会被忽略。这在开发新模块时非常有用。
if __name__ == "__main__":
# 在此处运行模块代码,如果模块直接被调用
对于Go语言,我使用一个test.go
文件和一个带有package main
的main.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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论