How to test a function's output (stdout/stderr) in unit tests

24,962

Solution 1

One thing to also remember, there's nothing stopping you from writing functions to avoid the boilerplate.

For example I have a command line app that uses log and I wrote this function:

func captureOutput(f func()) string {
    var buf bytes.Buffer
    log.SetOutput(&buf)
    f()
    log.SetOutput(os.Stderr)
    return buf.String()
}

Then used it like this:

output := captureOutput(func() {
    client.RemoveCertificate("www.example.com")
})
assert.Equal(t, "removed certificate www.example.com\n", output)

Using this assert library: http://godoc.org/github.com/stretchr/testify/assert.

Solution 2

You can do one of three things. The first is to use Examples.

The package also runs and verifies example code. Example functions may include a concluding line comment that begins with "Output:" and is compared with the standard output of the function when the tests are run. (The comparison ignores leading and trailing space.) These are examples of an example:

func ExampleHello() {
        fmt.Println("hello")
        // Output: hello
}

The second (and more appropriate, IMO) is to use fake functions for your IO. In your code you do:

var myPrint = fmt.Print

func (t *Thing) print(min_verbosity int, message string) {
    if t.verbosity >= minv {
        myPrint(message) // N.B.
    }
}

And in your tests:

func init() {
    myPrint = fakePrint // fakePrint records everything it's supposed to print.
}

func Test...

The third is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but bytes.Buffer in tests.

Share:
24,962
Flimzy
Author by

Flimzy

I coach small teams to benefit from DevOps, without the Enterprise scale. Contact me on my web site, or read my blog. You can also find me at: GitHub GitLab LinkedIn Twitter YouTube I am the author of Kivik which provides a common interface to CouchDB and PouchDB for Go and GopherJS. I am a contributor to the Apache CouchDB project, and a member of the CouchDB PMC.

Updated on December 03, 2021

Comments

  • Flimzy
    Flimzy over 2 years

    I have a simple function I want to test:

    func (t *Thing) print(min_verbosity int, message string) {
        if t.verbosity >= minv {
            fmt.Print(message)
        }
    }
    

    But how can I test what the function actually sends to standard output? Test::Output does what I want in Perl. I know I could write all my own boilerplate to do the same in Go (as described here):

    orig = os.Stdout
    r,w,_ = os.Pipe()
    thing.print("Some message")
    var buf bytes.Buffer
    io.Copy(&buf, r)
    w.Close()
    os.Stdout = orig
    if(buf.String() != "Some message") {
        t.Error("Failure!")
    }
    

    But that's a lot of extra work for every single test. I'm hoping there's a more standard way, or perhaps an abstraction library to handle this.