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

时间:2015-07-11 00:33:30

标签: unit-testing testing import go main

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

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

package main

import (
    "log"
)

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

func foo() string {
    return "Foo"
}

和我的main_test.go文件如下:

package main

import (
    "testing"
)

func Foo(t testing.T) {
    t.Error(foo())
}

当我运行go test main_test.go时,我得到了

# command-line-arguments
.\main_test.go:8: undefined: foo
FAIL    command-line-arguments [build failed]

据我了解,即使我将测试文件移到其他地方并尝试从main.go文件导入,我也无法导入它,因为它是package main

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

5 个答案:

答案 0 :(得分:16)

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

这是我的跑步:

$ ls
main.go     main_test.go
$ go test *.go
ok      command-line-arguments  0.003s

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

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

这是修改后的版本:

package main

import (
    "testing"
)

func TestFoo(t *testing.T) {
    t.Error(foo())
}

和修改后的输出:

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

答案 1 :(得分:9)

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

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

答案 2 :(得分:1)

这不是对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 ...

 }

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

请注意,err中的CombinedOutput()是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)

答案 3 :(得分:0)

如何使用标志测试 main 并断言退出代码

@MikeElis 的回答让我成功了一半,但是缺少一个主要部分,Go 自己的 flag_test.go 可以帮助我弄清楚。

免责声明

您本质上想要运行您的应用并测试正确性。因此,请随意标记此测试并将其归档在该类别中。但值得尝试这种类型的测试并看到好处。特别是如果您正在编写 CLI 应用程序。

这个想法是像往常一样运行 go test,并且

  1. 使用 go test 制作的应用的测试版本在子流程中“自身”运行单元测试(参见第 86 行)
  2. 我们还将环境变量(参见第 88 行)传递给子进程,该子进程将执行将运行 main 并导致测试以 main' 退出的代码部分退出代码:
    if os.Getenv(SubCmdFlags) != "" {
        // We're in the test binary, so test flags are set, lets reset it so
        // so that only the program is set
        // and whatever flags we want.
        args := strings.Split(os.Getenv(SubCmdFlags), " ")
        os.Args = append([]string{os.Args[0]}, args...)
    
        // Anything you print here will be passed back to the cmd.Stderr and
        // cmd.Stdout below, for example:
        fmt.Printf("os args = %v\n", os.Args)
    
        // Strange, I was expecting a need to manually call the code in
        // `init()`,but that seem to happen automatically. So yet more I have learn.
        main()
    }
    
    注意:如果 main 函数没有退出,测试将挂起/循环。
  3. 然后对从子流程返回的退出代码进行断言。
    // get exit code.
    got := cmd.ProcessState.ExitCode()
    if got != test.want {
        t.Errorf("got %q, want %q", got, test.want)
    }
    
    注意:在此示例中,如果返回预期退出代码以外的任何内容,测试将输出来自子进程的 STDOUT 和/或 STDERR,以帮助调试。

在此处查看完整示例:go-gitter: Testing the CLI

答案 4 :(得分:-3)

在两个来源中将包名称从main更改为foobar。 在src / foobar下移动源文件。

mkdir -p src/foobar
mv main.go main_test.go src/foobar/

确保将GOPATH设置为src / foobar所在的文件夹。

export GOPATH=`pwd -P`

测试
go test foobar