DEV Community

Shannon
Shannon

Posted on

10 2

Unit testing Stdout in Go

Figuring out how to unit test functions that print to the terminal can be something of a daunting task. Thankfully, it's actually pretty simple due to the magic of interfaces in Go!


This post is going to be short and sweet on how to test output by capturing it in a bytes.Buffer. Below, I have a function that accepts a string, prints out a statement, and then returns.

func SayHello(w io.Writer, name string) {
    fmt.Fprintf(w, "Hi, my name is %s\n", name)
}
Enter fullscreen mode Exit fullscreen mode

The critical component of this function is the io.Writer interface variable being passed in. Because it is an interface, this function will accept any data type that implements the method Write(b []byte) (n int, err error).

This interface is implemented in the io package and the entire code can be seen here. Most importantly, here is the code below for the interface:

type Writer interface {
    Write(p []byte) (n int, err error)
}
Enter fullscreen mode Exit fullscreen mode

So how do we print out this function to the terminal? We pass in os.Stdout, which has its own Write method! This can be seen here.

To print this out to our terminal, just add the following in the main() function.

func main() {
    SayHello(os.Stdout, "Shannon")
}
Enter fullscreen mode Exit fullscreen mode

Now, we can move onto unit testing this specific function, which just outputs a name and greeting. In order to test it, we need to capture its output and then turn it into a string from a slice of bytes. To do this, we'll utilize the bytes.Buffer struct, which has a Write method to satisfy the Writer interface.

Here is the full test code in order to compare these:

func TestSayHello(t *testing.T) {
    tt := []struct {
        description string
        name        string
        expect      string
    }{
        {"Empty", "", "Hi, my name is \n"},
        {"Name1", "Shannon", "Hi, my name is Shannon\n"},
        {"Name2", "Shug", "Hi, my name is Shug\n"},
    }

    for _, tc := range tt {
        t.Run(tc.description, func(t *testing.T) {
            var output bytes.Buffer
            SayHello(&output, tc.name)
            if tc.expect != output.String() {
                t.Errorf("got %s but expected %s", output.String(), tc.expect)
            }
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

You'll notice that we are declaring a Buffer here: var output bytes.Buffer and then passing that buffer into the SayHello function. However, we are actually passing in the address of the buffer. This is due to the Buffer method for Write being a pointer receiver, which can be seen here.

And that's it! Run go test -v to see that all tests pass, and you have successfully captured the output of a command in order to unit test the output without actually returning the string value.

Postmark Image

Speedy emails, satisfied customers

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (2)

Collapse
 
mstgnz profile image
Mesut GENEZ

So how do we capture print inside a void function that has no "writer" parameter?

func SayHello(name string) {
fmt.Print("Hi, my name is", name)
}

Collapse
 
reprintsev profile image
Aleksey Reprintsev

You can do it with example function from testing package:

func ExampleSayHello() {
    fmt.Println("Alex")
    // Output: Hi, my name is Alex"
}
Enter fullscreen mode Exit fullscreen mode

(btw, the result may be unexpected for your function: go.dev/play/p/J4zY09qEU4X )

Or use os.Pipe() as described in this discussion, but be aware of buffer overflows and data race.

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay