如何在golang中测试主包函数?

The*_*chu 31 testing import unit-testing program-entry-point go

我想测试一下我的主程序包中包含的一些函数,但我的测试似乎无法访问这些函数.

我的示例main.go文件如下所示:

package main

import (
    "log"
)

func main() {
    log.Printf(foo())
}

func foo() string {
    return "Foo"
}
Run Code Online (Sandbox Code Playgroud)

和我的main_test.go文件看起来像:

package main

import (
    "testing"
)

func Foo(t testing.T) {
    t.Error(foo())
}
Run Code Online (Sandbox Code Playgroud)

当我跑步时,go test main_test.go我得到了

# command-line-arguments
.\main_test.go:8: undefined: foo
FAIL    command-line-arguments [build failed]
Run Code Online (Sandbox Code Playgroud)

据我所知,即使我将测试文件移到别处并尝试从main.go文件导入,我也无法导入它,因为它是package main.

构建此类测试的正确方法是什么?我应该从main包中删除所有内容,除了一个简单的主函数来运行所有内容然后在自己的包中测试函数,还是有办法让我在测试期间从主文件中调用这些函数?

Dav*_*rth 20

在命令行上指定文件时,必须指定所有文件

这是我的运行:

$ ls
main.go     main_test.go
$ go test *.go
ok      command-line-arguments  0.003s
Run Code Online (Sandbox Code Playgroud)

请注意,在我的版本中,我在命令行上同时运行了main.go和main_test.go

此外,您的_test文件不太正确,您需要将测试函数称为TestXXX并指向测试.T

这是修改过的verison:

package main

import (
    "testing"
)

func TestFoo(t *testing.T) {
    t.Error(foo())
}
Run Code Online (Sandbox Code Playgroud)

和修改后的输出:

$ go test *.go
--- FAIL: TestFoo (0.00s)
    main_test.go:8: Foo
FAIL
FAIL    command-line-arguments  0.003s
Run Code Online (Sandbox Code Playgroud)

  • 为什么要指定任何文件来"去测试"?别. (6认同)
  • 是的,但我认为询问的角色有点困难,我不想将问题与设置 GOPATH 或类似的东西混淆。进行“测试”。会工作?(之前没有在 GOPATH 之外尝试过) (3认同)

Zan*_*ynx 11

单元测试只到目前为止.在某些时候,你必须实际运行该程序.然后你测试它是否与实际输入一起工作,从真实来源,产生实际输出到真实目的地.真的.

如果你想对单元进行单元测试,请将其移出main().

  • 我不想在这里发动代码覆盖率大战,但我不同意100%覆盖率是海市rage楼的说法。务实地说,是的,有时由于满足覆盖范围政策而导致软件延迟交付的成本要超过不覆盖手动测试代码的成本。但是,当您说无法模拟文件关闭错误时,这实际上是设计缺陷。这是接口在Go中派上用场的地方。例如,“ func Foo(file os.File)”不如“ func Foo(file io.ReadWriteCloser)”那样容易测试。 (5认同)
  • 但是要实现100%的覆盖率,需要测试主要功能,如何实现? (3认同)
  • @perrohunter 100%的测试覆盖率是一个海市rage楼。不要理会它。我最讨厌的是不切实际的测试覆盖率政策,有一次我在文件关闭之类的内容中添加了if错误,这降低了测试覆盖率,因为我们无法模拟文件关闭错误。它可能未经测试,但比忽略错误并假装不会发生要好100%! (3认同)

Mik*_*lis 6

不是对 OP 问题的直接回答,我大体上同意先前的回答和评论,敦促他们main应该主要是打包函数的调用者。话虽如此,这是我发现对测试可执行文件有用的方法。它利用log.Fatalnexec.Command

  1. main.go使用延迟函数写入,该函数调用 log.Fatalln() 在返回之前将消息写入 stderr。
  2. 在 中main_test.go,使用exec.Command(...)cmd.CombinedOutput()运行您的程序,并选择参数来测试某些预期结果。

例如:

func main() {
    // Ensure we exit with an error code and log message
    // when needed after deferred cleanups have run.
    // Credit: https://medium.com/@matryer/golang-advent-calendar-day-three-fatally-exiting-a-command-line-tool-with-grace-874befeb64a4
    var err error
    defer func() {
        if err != nil {
            log.Fatalln(err)
        }
    }()

    // Initialize and do stuff

    // check for errors in the usual way
    err = somefunc()
    if err != nil {
        err = fmt.Errorf("somefunc failed : %v", err)
        return
    }

    // do more stuff ...

 }
Run Code Online (Sandbox Code Playgroud)

例如main_test.go,对于应该导致somefunc失败的错误参数的测试可能如下所示:

func TestBadArgs(t *testing.T) {
    var err error
    cmd := exec.Command(yourprogname, "some", "bad", "args")
    out, err := cmd.CombinedOutput()
    sout := string(out) // because out is []byte
    if err != nil && !strings.Contains(sout, "somefunc failed") {
        fmt.Println(sout) // so we can see the full output 
        t.Errorf("%v", err)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,errfromCombinedOutput()是来自 log.Fatalln 的后台调用的非零退出代码os.Exit(1)。这就是为什么我们需要使用outsomefunc.

exec软件包还提供cmd.Runcmd.Output. 这些可能比cmd.CombinedOutput某些测试更合适。我还发现有一个TestMain(m *testing.M)在运行测试之前和之后进行设置和清理的函数很有用。

func TestMain(m *testing.M) {
    // call flag.Parse() here if TestMain uses flags
    os.Mkdir("test", 0777) // set up a temporary dir for generate files

    // Create whatever testfiles are needed in test/

    // Run all tests and clean up
    exitcode := m.Run()
    os.RemoveAll("test") // remove the directory and its contents.
    os.Exit(exitcode)
Run Code Online (Sandbox Code Playgroud)