Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test Go function containing log.Fatal()

Tags:

testing

go

Say, I had the following code that prints some log messages. How would I go about testing that the correct messages have been logged? As log.Fatal calls os.Exit(1) the tests fail.

package main  import (     "log" )  func hello() {     log.Print("Hello!") }  func goodbye() {     log.Fatal("Goodbye!") }  func init() {     log.SetFlags(0) }  func main() {     hello()     goodbye() } 

Here are the hypothetical tests:

package main  import (     "bytes"     "log"     "testing" )   func TestHello(t *testing.T) {     var buf bytes.Buffer     log.SetOutput(&buf)      hello()      wantMsg := "Hello!\n"     msg := buf.String()     if msg != wantMsg {         t.Errorf("%#v, wanted %#v", msg, wantMsg)     } }  func TestGoodby(t *testing.T) {     var buf bytes.Buffer     log.SetOutput(&buf)      goodbye()      wantMsg := "Goodbye!\n"     msg := buf.String()     if msg != wantMsg {         t.Errorf("%#v, wanted %#v", msg, wantMsg)     } } 
like image 520
andrewsomething Avatar asked Jun 06 '15 23:06

andrewsomething


People also ask

How do you test a Go function?

At the command line in the greetings directory, run the go test command to execute the test. The go test command executes test functions (whose names begin with Test ) in test files (whose names end with _test.go). You can add the -v flag to get verbose output that lists all of the tests and their results.

What does log fatal do in Go?

log. Fatal makes use of os. Exit and is best called when an error is irreversible and may affect the entire program.

Which command is used to run the tests in Go?

After the package test finishes, go test prints a summary line showing the test status ('ok' or 'FAIL'), the package name, and elapsed time. To run your tests in this mode, run go test in your project's root directory. In the package list mode, go test compiles and tests each package listed as arguments to the command.


2 Answers

This is similar to "How to test os.Exit() scenarios in Go": you need to implement your own logger, which by default redirect to log.xxx(), but gives you the opportunity, when testing, to replace a function like log.Fatalf() with your own (which does not call os.Exit(1))

I did the same for testing os.Exit() calls in exit/exit.go:

exiter = New(func(int) {}) exiter.Exit(3) So(exiter.Status(), ShouldEqual, 3) 

(here, my "exit" function is an empty one which does nothing)

like image 168
VonC Avatar answered Oct 14 '22 06:10

VonC


While it's possible to test code that contains log.Fatal, it is not recommended. In particular you cannot test that code in a way that is supported by the -cover flag on go test.

Instead it is recommended that you change your code to return an error instead of calling log.Fatal. In a sequential function you can add an additional return value, and in a goroutine you can pass an error on a channel of type chan error (or some struct type containing a field of type error).

Once that change is made your code will be much easier to read, much easier to test, and it will be more portable (now you can use it in a server program in addition to command line tools).

If you have log.Println calls I also recommend passing a custom logger as a field on a receiver. That way you can log to the custom logger, which you can set to stderr or stdout for a server, and a noop logger for tests (so you don't get a bunch of unnecessary output in your tests). The log package supports custom loggers, so there's no need to write your own or import a third party package for this.

like image 30
voutasaurus Avatar answered Oct 14 '22 08:10

voutasaurus