Go语言 如何测试主要软件包功能

nle07wnf  于 2023-01-28  发布在  Go
关注(0)|答案(6)|浏览(121)

我想测试主包中包含的几个函数,但我的测试似乎无法访问这些函数。
我的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包中删除所有的东西,只留下一个简单的main函数来运行所有的东西,然后在它们自己的包中测试这些函数,还是有一种方法可以让我在测试过程中从main文件中调用这些函数?

zzwlnbp8

zzwlnbp81#

在命令行上指定文件时,必须指定所有文件
以下是我的跑步记录:

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

注意,在我的版本中,我在命令行上运行了main.go和main_test.go
另外,your _test文件不太正确,您需要将test函数命名为TestXXX,并获取指向testing.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
8tntrjer

8tntrjer2#

单元测试也就到此为止了。在某个时候你必须实际运行程序。然后你测试它是否能用真实的输入,从真实的源,产生真实的输出到真实的目的地。真的。
如果你想对一个东西进行单元测试,请把它移出main()。

xytpbqjk

xytpbqjk3#

不是OP问题的直接答案,我大体上同意之前的答案和评论,即main应该主要是打包函数的调用者。也就是说,我发现这里有一个测试可执行文件的有用方法。它使用log.Fatalnexec.Command
1.使用延迟函数编写main.go,该函数调用log.fatalln(),以便在返回之前将消息写入stderr。
1.在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 from 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)
o8x7eapl

o8x7eapl4#

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

@MikeElis的答案让我走了一半,但有一个主要部分是围棋自己的flag--test,去帮我弄清楚。

免责声明

你本质上是想运行你的应用程序并测试正确性。所以请随意标记此测试并将其归入该类别。但值得尝试这种类型的测试并看到其好处。特别是如果你正在编写CLI应用程序。
其思想是照常运行go test,并且
1.使用go test生成的应用程序的测试构建版本,在子流程中运行"自身"单元测试(参见第86行)
1.我们还将环境变量(参见第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()
}

注解:如果主功能未退出,则测试将挂起/循环。
1.然后Assert从子进程返回的退出代码。

// get exit code.
got := cmd.ProcessState.ExitCode()
if got != test.want {
    t.Errorf("got %q, want %q", got, test.want)
}

注意:在本例中,如果返回的不是预期的退出代码,测试将从子进程输出STDOUT和/或STDERR,以帮助调试。
完整示例见此处:go-gitter:测试CLI

ahy6op9u

ahy6op9u5#

因为您只为测试设置了一个文件,所以它不会使用其他的go文件。
运行go test而不是go test main_test.go
同时将测试函数签名Foo(t testing.T)更改为TestFoo(t *testing.T)

xtupzzrd

xtupzzrd6#

在两个源文件中将包名从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

相关问题