Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

stdout

testing

go

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.

like image 403
Flimzy Avatar asked Nov 07 '14 15:11

Flimzy


2 Answers

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.

like image 159
Caleb Avatar answered Sep 22 '22 20:09

Caleb


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.

like image 38
Ainar-G Avatar answered Sep 19 '22 20:09

Ainar-G