1

I have implemented a type wrapping glog so that I can add a prefix to log message identifying the emitter of the log in my program and I can change the log level per emitter.

How could I implement the unit tests ? The problem is that glog outputs text to stdErr.

The code is trivial but I would like the have the unit test and 100% coverage like the rest of the code. This programming effort already payed.

chmike
  • 20,922
  • 21
  • 83
  • 106
  • 1
    The glog output (if you're referring to [`github.com/golang/glog`](https://godoc.org/github.com/golang/glog)) is configurable. Could you output to files rather than stderr? – JimB Dec 20 '16 at 17:32
  • 1
    you could capture stderr in test like stdout is captured in this [answer](http://stackoverflow.com/a/10476304/1024794) – Maxim Yefremov Dec 20 '16 at 20:36
  • @JimB that would work. But, but it would be difficult to test what has been output by each call of my wrapper methods. – chmike Dec 23 '16 at 09:47
  • The suggestion of @Maxim Yefremov is what I was looking for. It's looking great. Please write an answer so I can assign you the asnswer. – chmike Dec 23 '16 at 09:50

2 Answers2

4

Test which captures stderr:

package main

import (
    "bytes"
    "io"
    "os"
    "testing"

    "github.com/golang/glog"
    "strings"
)

func captureStderr(f func()) (string, error) {
    old := os.Stderr // keep backup of the real stderr
    r, w, err := os.Pipe()
    if err != nil {
        return "", err
    }
    os.Stderr = w

    outC := make(chan string)
    // copy the output in a separate goroutine so printing can't block indefinitely
    go func() {
        var buf bytes.Buffer
        io.Copy(&buf, r)
        outC <- buf.String()
    }()

    // calling function which stderr we are going to capture:
    f()

    // back to normal state
    w.Close()
    os.Stderr = old // restoring the real stderr
    return <-outC, nil
}

func TestGlogError(t *testing.T) {
    stdErr, err := captureStderr(func() {
        glog.Error("Test error")
    })
    if err != nil {
        t.Errorf("should not be error, instead: %+v", err)
    }
    if !strings.HasSuffix(strings.TrimSpace(stdErr), "Test error") {
        t.Errorf("stderr should end by 'Test error' but it doesn't: %s", stdErr)
    }
}

running test:

go test -v
=== RUN   TestGlogError
--- PASS: TestGlogError (0.00s)
PASS
ok      command-line-arguments  0.007s
Maxim Yefremov
  • 13,671
  • 27
  • 117
  • 166
0

Write an interface that describes your usage. This won't be very pretty if you use the V method, but you have a wrapper so you've already done the hard work that fixing that would entail.

For each package you need to test, define

type Logger interface {
    Infoln(...interface{}) // the methods you actually use in this package
}

And then you can easily swap it out by not referring to glog types directly in your code.

nothingmuch
  • 1,456
  • 9
  • 10
  • 1
    I already did that. This will allow to test use of my wrapper. Perfect suggestion. The question is how to test the wrapper itself. – chmike Dec 23 '16 at 09:42