单元如何测试主要包的功能



我想测试主包中包含的几个函数,但是我的测试似乎无法访问这些函数。

我的示例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]

据我所知,即使我将测试文件移到其他地方并尝试从主程序导入。我无法导入它,因为它是package main

构建这种测试的正确方法是什么?我应该只是删除一切从main包除了一个简单的主函数运行一切,然后在自己的包测试的功能,或者有一种方法让我从主文件中调用这些函数在测试期间?

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

这是我的运行:

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

注意,在我的版本中,我使用了两个main。Go和main_test。进入命令行

同样,你的_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

单元测试只能到此为止。在某些时候,你必须实际运行程序。然后测试它是否适用于真实的输入,来自真实的来源,产生真实的输出到真实的目的地。真的。

如果你想对一个东西进行单元测试,把它移出main()。

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

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

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

@MikeElis的回答让我完成了一半,但是有一个重要的部分缺失,那就是Go自己的flag_test。去帮我想想。

免责声明

你本质上想要运行你的应用程序并测试正确性。所以请给这个测试贴上你想要的标签,并把它归入那个类别。但值得尝试这种类型的测试,看看它的好处。尤其是当你在写一个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 = %vn", 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

因为您只为测试设置了一个文件,所以它不会使用其他go文件。

go test代替go test main_test.go

同时修改测试函数签名Foo(t testing.T)TestFoo(t *testing.T)

将两个源代码中的包名从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

相关内容

  • 没有找到相关文章

最新更新