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

后端 未结 3 671
轮回少年
轮回少年 2020-11-28 12:13

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         


        
相关标签:
3条回答
  • 2020-11-28 12:30

    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.

    0 讨论(0)
  • 2020-11-28 12:43

    You can do one of two 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...
    

    Another option is to use fmt.Fprintf with an io.Writer that is os.Stdout in production code, but may be say bytes.Buffer in tests.

    0 讨论(0)
  • 2020-11-28 12:47

    You could consider adding a return statement to your function to return the string that is actually printed out.

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

    Now, your test could just check the returned string against an expected string (rather than the print out). Maybe a bit more in-line with Test Driven Development (TDD).

    And, in your production code, nothing would need to change, since you don't have to assign the return value of a function if you don't need it.

    0 讨论(0)
提交回复
热议问题