我可以从TestMain输出日志消息吗?

huangapple go评论59阅读模式
英文:

Can I output log messages from TestMain?

问题

我想编写一个集成测试,测试将数据写入和从数据库中读取的功能。为了做到这一点,我需要我的第一个测试函数连接并准备一个测试数据库;如果成功,让其他测试运行,这些测试将在其上执行查询。

因此,我实现了一个TestMain(m *testing.M)函数,它创建一个数据库并在其上运行迁移,如果没有错误,我就运行m.Run()

但是它没有像testing.T那样的m.Printm.Fatal方法。如果我尝试使用log.Print()记录一些内容,它不会输出(可能是因为没有测试能够正确地标记为失败)。

而且,我不喜欢使用常规测试函数创建数据库的原因是,常规测试当然不能阻止其他测试运行,因此该文件中的所有其他测试都将运行并干扰输出消息,因为它们都会失败。

那么,如何从TestMain中输出日志?我的TestMain仍然可以以多种方式失败,我希望在输出中看到失败的原因。

示例展示了TestMain的输出未显示的情况:

$ ls
go.mod		main_test.go
$ cat go.mod
module App

go 1.16
$ cat main_test.go 
package main

import (
	"testing"
	"log"
)

func TestMain(m *testing.M) {
	log.Print("hello")
}
$ go test ./.
ok  	App	0.147s
$ 
英文:

I want to write an integration test which tests writing and reading data to and from a database. In order to do this, I need my first test function to connect and prepare a test database; and if that is a success, let the other tests run, which will then be doing queries on it.

So I implemented a TestMain(m *testing.M) which creates a database and runs migrations on it, and if there's no errors, I run m.Run().

But it doesn't have any m.Print or m.Fatal methods like testing.T does. And if I try to log something with log.Print(), it doesn't get output (probably because no testing could have been properly marked as failing).

And the reason why I don't like the creation of the database with a regular test function, is because a regular test of course cannot stop other tests from running, so all my other tests from that file will run and plague the output messages, since they will all fail.

So how do I log something out from TestMain? My TestMain can still fail in multiple ways, and i would like to see in the output what it was.

Example to show TestMain's output not being displayed:

$ ls
go.mod		main_test.go
$ cat go.mod
module App

go 1.16
$ cat main_test.go 
package main

import (
	"testing"
	"log"
)

func TestMain(m *testing.M) {
	log.Print("hello")
}
$ go test ./.
ok  	App	0.147s
$ 

答案1

得分: 1

你需要使用-v标志:

go test -v .

注意:.等同于./.

如果你想在所有子目录中运行测试,可以使用./...,像这样:

go test -v ./...

为什么要这样做?因为你是在所谓的包列表模式下运行测试(即./.)- 根据go help test中的文档:

> 在这种(包列表)模式下,go test会编译和测试命令行上列出的每个包。如果一个包的测试通过,go test只打印最后的'ok'摘要行。如果一个包的测试失败,go test会打印完整的测试输出。

这就是你所注意到的行为。继续阅读:

> ... 如果使用了-bench或-v标志,go test会打印完整的输出,即使是通过的包测试,以显示请求的基准结果或详细日志记录。


**注意:**仅使用go test(即没有包列表)将允许从TestMain中输出日志,如log.Print


编辑
以下是我通常为我的测试进行预先设置的步骤:

var testDB     *pgxpool.Pool // 所有测试都应该使用这个

func createDBconn() (*pgxpool.Pool, error) {
    // 读取环境变量 - 建立连接
    // ...
    return dbconn, nil
}

func TestMain(m *testing.M) {
    var err error
	
	testDB, err = createDBconn()
	if err != nil {
		log.Fatal(err)
	}

	// 如果TestMain使用了flags,则在这里调用flag.Parse()
	os.Exit(m.Run())
}

如果任何准备工作失败,那么log.Fatal会确保记录原因并终止程序。

英文:

You need the -v flag:

go test -v .

Note: . is equivalent to ./.

If you want to run tests on all sub-directories use ./... like so:

go test -v ./...

Why is this? Since you are running tests in what's call package list mode (i.e. ./.) - from the docs in go help test:

> In this (package list) mode, go test compiles and tests each of the packages listed
> on the command line. If a package test passes, go test prints only the
> final 'ok' summary line. If a package test fails, go test prints the
> full test output.

This is the behavior you have noted. Reading further:

> ... If invoked with the -bench or -v flag, go
> test prints the full output even for passing package tests, in order
> to display the requested benchmark results or verbose logging.


Note: go test alone (i.e. no package list) will allow output logging from TestMain like log.Print.


EDIT
here's how I typically do pre-flight setup for my tests:

var testDB     *pgxpool.Pool // all tests should use this

func createDBconn() (*pgxpool.Pool, error) {
    // read ENV VARs - establish connection
    // ...
    return dbconn, nil
}

func TestMain(m *testing.M) {
    var err error
	
	testDB, err = createDBconn()
	if err != nil {
		log.Fatal(err)
	}

	// call flag.Parse() here if TestMain uses flags
	os.Exit(m.Run())
}

if any of the prep fails, then log.Fatal ensures the reason is logged & the program terminates.

答案2

得分: 1

似乎使用log.Print()打印的消息只会在测试以非零代码退出时显示,所以最好在日志行后立即以非零退出。代码如下:

log.Print("数据库设置失败")
os.Exit(1)
英文:

It seems that messages printed with log.Print() will display if the test exits with a non-zero code, so this situation is best solved by exiting with non-zero right after the log line, like so:

log.Print("setup of database has failed")
os.Exit(1)

huangapple
  • 本文由 发表于 2022年3月18日 07:23:38
  • 转载请务必保留本文链接:https://go.coder-hub.com/71520143.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定