Home > Software engineering >  How to print from go test to console without using verbose option
How to print from go test to console without using verbose option

Time:05-11

I want to print an informative message to console during a test, but not have the verbose test output.

I have tried printing from the test using:

  • fmt.Println("my message") // STDOUT
  • fmt.Fprintln(os.Stderr, "my message") // STDERR
  • t.Log("my message\n") // testing.T log

which all produce the same effect of showing in the console if go test -v is used, but not showing if just go test is used.

However, with go test -v, I also get all the verbose test output like:

=== RUN   My_Test
--- PASS: My_Test (0.07s)

go help testflag says:

-v
    Verbose output: log all tests as they are run. Also print all
    text from Log and Logf calls even if the test succeeds.

but what I want is to not log all tests as they are run, but still print all text from Log and Logf calls even if the test succeeds

Is there a way to print a visible message from within a test, but not see the RUN and PASS messages?

CodePudding user response:

The testing framework "hijacks" the standard output and error streams for obvious reasons. So no matter what, whether writing to those streams appears in the output is controlled by the testing framework, and it provides no means to "customize" it other than showing or hiding all using the -v flag.

What you may do is use the -json testing flag:

-json
    Log verbose output and test results in JSON. This presents the
    same information as the -v flag in a machine-readable format.

So you get all the output you would otherwise get with -v, but you have a separate JSON object for each line.

Having this test function:

func TestMy_Test(t *testing.T) {
    fmt.Println("[custom] my message from fmt.Println")
}

Output of go test -v .

=== RUN   TestMy_Test
[custom] my message from fmt.Println
--- PASS: TestMy_Test (0.00s)
PASS
ok      github.com/icza/play    0.002s

Output of go test -json .

{"Time":"2022-05-10T09:26:26.712800797 02:00","Action":"run","Package":"github.com/icza/play","Test":"TestMy_Test"}
{"Time":"2022-05-10T09:26:26.71293072 02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"=== RUN   TestMy_Test\n"}
{"Time":"2022-05-10T09:26:26.712946548 02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}
{"Time":"2022-05-10T09:26:26.712954637 02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"--- PASS: TestMy_Test (0.00s)\n"}
{"Time":"2022-05-10T09:26:26.712958774 02:00","Action":"pass","Package":"github.com/icza/play","Test":"TestMy_Test","Elapsed":0}
{"Time":"2022-05-10T09:26:26.712964812 02:00","Action":"output","Package":"github.com/icza/play","Output":"PASS\n"}
{"Time":"2022-05-10T09:26:26.713170439 02:00","Action":"output","Package":"github.com/icza/play","Output":"ok  \tgithub.com/icza/play\t0.002s\n"}
{"Time":"2022-05-10T09:26:26.713573313 02:00","Action":"pass","Package":"github.com/icza/play","Elapsed":0.003}

You can write a simple app that processes and filters these JSON objects. Or you can filter the output as you could filter any other output.

Output of go test -json . |grep '\[custom\]'

{"Time":"2022-05-10T09:28:24.197077681 02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}

If you also want the "pass" or "fail" messages, run go test -json . |grep '"pass"\|"fail"\|\[custom\]'

{"Time":"2022-05-10T09:29:26.069181336 02:00","Action":"output","Package":"github.com/icza/play","Test":"TestMy_Test","Output":"[custom] my message from fmt.Println\n"}
{"Time":"2022-05-10T09:29:26.069189228 02:00","Action":"pass","Package":"github.com/icza/play","Test":"TestMy_Test","Elapsed":0}
{"Time":"2022-05-10T09:29:26.069199239 02:00","Action":"pass","Package":"github.com/icza/play","Elapsed":0}

CodePudding user response:

You can create you own Log function and use it for printing on screen

func Log(args ...interface{}) {
    fmt.Fprintln(os.Stdout, args...)
}

You can also make it to print log based on condition passed through flag

var p = flag.Bool("p", false, "Enable Local Logging")

func MyLog(args ...interface{}) {
  if *p {
    fmt.Fprintln(os.Stdout, args...)
  }
}

Example

package main

import (
    "fmt"
    "testing"
    "os"
    "flag"
)

var p = flag.Bool("p", false, "Enable Local Logging")

func Log(args ...interface{}) {
  if *p {
    fmt.Fprintln(os.Stdout, args...)
  }
}

func IntMin(a, b int) int {
    if a < b {
        return a
    }
    return b
}

func TestIntMinBasic(t *testing.T) {
    ans := IntMin(2, -2)
    if ans != -2 {
        t.Errorf("IntMin(2, -2) = %d; want -2", ans)
    }
}

func TestIntMinTableDriven(t *testing.T) {
    var tests = []struct {
        a, b int
        want int
    }{
        {0, 1, 0},
        {1, 0, 0},
        {2, -2, -2},
        {0, -1, -1},
        {-1, 0, -1},
    }

    Log("Print to Screen")

    for _, tt := range tests {

        testname := fmt.Sprintf("%d,%d", tt.a, tt.b)
        t.Run(testname, func(t *testing.T) {
            ans := IntMin(tt.a, tt.b)
            if ans != tt.want {
                t.Errorf("got %d, want %d", ans, tt.want)
            }
        })
    }
}

func BenchmarkIntMin(b *testing.B) {
    for i := 0; i < b.N; i   {
        IntMin(1, 2)
    }
}

And to pass the flag you can use -args

-args Pass the remainder of the command line (everything after -args) to the test binary, uninterpreted and unchanged. Because this flag consumes the remainder of the command line, the package list (if present) must appear before this flag.

Cmd Example:

go test -args -p
  • Related