Testing mocks and overloading?

Hi,
I’d like to create a missing test for fmt.Println.
Something like
func TestPrintln(t testing.T) {
var testData = "Test data"
var orgOutput = os.Stdout // What am I getting here ?!?
os.Stdout = myObject // I guess that it won’t work as “fmt” imports “os” itself
fmt.Print(testData)
if myObject.printedContent != testData {
t.Fail()
}
os.Stdout = orgOutput
}

I know how to do it in other languages but I have no idea where to start with GO. Except I can use another “entity” like a shell process to run go code in a sandbox and check its output.
Any idea on where to start?

I would hope that fmt.Println is fairly well tested already.

Assuming that this is just a placeholder for some other thing you have that produces output that you want to test, look into giving it an io.Writer, like fmt.Fprintln. This way you can pass it os.Stdout to write to stdout, or you can pass it a *bytes.Buffer to write into a buffer you can check later, etc.

If you actually want to replace os.Stdout this is unfortunately more annoying than you’d hope as it has type *os.File and not a more relaxed interface type.

1 Like

Yes, you are very correct about your assumption.
My goal was to create an implementation for something similar and I found out that there are no tests for fmt.Println.
One thing I’d like to achieve is to overload os.Stdout and os.Stderr with /dev/null for application under test (blackbox) so that this output won’t get printed. I already realized that mocking, overloading and many other things present in “other languages” are more than challenging in golang.

Not necessarily that challenging. Looking at Printf as your example, you wouldn’t test Printf, you would test Fprintf. For example, just typing into the browser and not necessarily representative of what the stdlib actually does,

func TestFormatInt(t *testing.T) {
    buf := new(bytes.Buffer)
    n, err := fmt.Fprintf(buf, "%d", 42)
    if err != nil { ... }
    if n != 2 { ... }
    if buf.String() != "42" { ... }
}

We now know Fprintf can format at least one integer correctly. With Printf defined as

func Printf(format string, args ...interface{}) (int, error) {
    return Fprintf(os.Stdout, format, args...)
}    

(and this is actually the implementation in the stdlib) we don’t really need to test Printf much.

Generally this sets a pattern for testable code: take dependencies as interfaces via parameters. Don’t use globals. For things that do use globals, like Printf, make it a thin wrapper around something that doesn’t. I don’t think this differs significantly in Go compared to anything else.

For you own code, don’t muck around with redirecting os.Stdout and os.Stderr. Make your code take io.Writers and pass them something during testing and os.Stdout in the real program, as appropriate.

In this specific case a *bytes.Buffer “mocks” os.Stdout as they are both io.Writers.

3 Likes

And that just made my feelings about testability in GO stronger: there are some cases which are very difficult to test and in this case fmt.Printf wouldn’t be applicable for testing but we can do it with fmt.Sprintf and fmt.Fprintf. The same situation would probably be for anything that calls os.Exit (ex. log.Fatal).
I guess that I must get used to how different GO seems to be from other languages.
Many thanks for the answers!

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.