how to deal with the "fmt" golang library package for CLI testing - testing

Disclaimer: I wish you a merry XMas and I hope my question does not disturb you!
sample.go:
package main
import(
"fmt"
"os"
)
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Println("Error: something is wrong!")
os.Exit(1)
} else {
fmt.Println("May the force be with you!")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: something is wrong!
// exit status 1
I did a lot of research on how to use interfaces in golang testing. But so far I was not able to wrap my head around this completely. At least I can not see how interfaces help me when I need to "mock" (apologies for using this word) golang std. library packages like "fmt".
I came up with two scenarios:
use os/exec to test the command line interface
wrap fmt package so I have control and am able to check the output strings
I do not like both scenarios:
I experience going through the actual command line a convoluted and not-performant (see below). Might have portability issues, too.
I believe this is the way to go but I fear that wrapping the fmt package might be a lot of work (at least wrapping the time package for testing turned out a non-trivial task (https://github.com/finklabs/ttime)).
Actual Question here: Is there another (better/simpler/idiomatic) way?
Note: I want to do this in pure golang, I am not interested in the next testing framework.
cli_test.go:
package main
import(
"os/exec"
"testing"
)
func TestCli(t *testing.T) {
out, err := exec.Command("go run sample.go").Output()
if err != nil {
t.Fatal(err)
}
if string(out) != "May the force be with you!\nError: this is broken and not useful!\nexit status 1" {
t.Fatal("There is something wrong with the CLI")
}
}

Chapter 11 of Kerningham's Book gives a good solution to this question.
The trick is to change the calls to fmt.Printline() to calls to
fmt.Fprint(out, ...) where out is initialised to os.Stdout
This can be overwritten in the test harness to new(bytes.Buffer) allowing the
test to capture the output.
See https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo.go and
https://github.com/adonovan/gopl.io/blob/master/ch11/echo/echo_test.go
edited by OP...
sample.go:
package main
import(
"fmt"
"os"
"io"
)
var out io.Writer = os.Stdout // modified during testing
var exit func(code int) = os.Exit
type sample struct {
value int64
}
func (s sample) useful() {
if s.value == 0 {
fmt.Fprint(out, "Error: something is wrong!\n")
exit(1)
} else {
fmt.Fprint(out, "May the force be with you!\n")
}
}
func main() {
s := sample{42}
s.useful()
s.value = 0
s.useful()
}
// output:
// May the force be with you!
// Error: this is broken and not useful!
// exit status 1
cli_test.go:
package main
import(
"bytes"
"testing"
)
func TestUsefulPositive(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
s := sample{42}
s.useful()
if out.(*bytes.Buffer).String() != "May the force be with you!\n" {
t.Fatal("There is something wrong with the CLI")
}
}
func TestUsefulNegative(t *testing.T) {
bak := out
out = new(bytes.Buffer)
defer func() { out = bak }()
code := 0
osexit := exit
exit = func(c int) { code = c }
defer func() { exit = osexit }()
s := sample{0}
s.useful()
if out.(*bytes.Buffer).String() != "Error: something is wrong!\n" {
t.Fatal("There is something wrong with the CLI")
}
if code != 1 {
t.Fatal("Wrong exit code!")
}
}

Am I missing something here or are you talking of testable examples?
Basically, it works like this: In a *_test.go file, you need to adhere to the convention Example[[T][_M]] where T is a placeholder for the type and M a placeholder for the method you want to display the testable example as example code in the Godoc. If the function is just called Example(), the code will be shown as a package example.
Below the last line of the code of your example, you can put a comment like this
// Output:
// Foo
Now go test will make sure that the testable example function either exactly puts out everything below // Output: (including whitespace) or it will make the test fail.
Here is an actual example for an testable example
func ExampleMongoStore_Get() {
sessionId := "ExampleGetSession"
data, err := ms.Get(sessionId)
if err == sessionmw.ErrSessionNotFound {
fmt.Printf("Session '%s' not found\n", sessionId)
data = make(map[string]interface{})
data["foo"] = "bar"
ms.Save(sessionId, data)
}
loaded, _ := ms.Get(sessionId)
fmt.Printf("Loaded value '%s' for key '%s' in session '%s'",
loaded["foo"],
"foo", sessionId)
// Output:
// Session 'ExampleGetSession' not found
// Loaded value 'bar' for key 'foo' in session 'ExampleGetSession'
}
Edit: Have a look at the output of above example at godoc.org

Related

Go validator with sql null types?

I am having problems getting the golang validator to work with SQL null types. Here's an example of what I tried:
package main
import (
"database/sql"
"database/sql/driver"
"log"
"gopkg.in/go-playground/validator.v9"
)
// NullInt64
type NullInt64 struct {
sql.NullInt64
Set bool
}
func MakeNullInt64(valid bool, val int64) NullInt64 {
n := NullInt64{}
n.Set = true
n.Valid = valid
if valid {
n.Int64 = val
}
return n
}
func (n *NullInt64) Value() (driver.Value, error) {
if !n.NullInt64.Valid {
return nil, nil
}
return n.NullInt64.Int64, nil
}
type Thing struct {
N2 NullInt64 `validate:"min=10"`
N3 int64 `validate:"min=10"`
N4 *int64 `validate:"min=10"`
}
func main() {
validate := validator.New()
n := int64(6)
number := MakeNullInt64(true, n)
thing := Thing{number, n, &n}
e := validate.Struct(thing)
log.Println(e)
}
When I run this code, I only get this output:
Key: 'Thing.N3' Error:Field validation for 'N3' failed on the 'min'
tag
Key: 'Thing.N4' Error:Field validation for 'N4' failed on the
'min' tag
The problem is that I want it to also show that Thing.N2 failed for the same reasons as Thing.N3 and Thing.N4.
I tried introducing the func (n *NullInt64) Value() method because it was mentioned in the documentation. But I think I misunderstood something. Can anyone tell me what I did wrong?
UPDATE
There is an Example specifically for that. You may check it out. My other proposed solution should still work though.
Since the value you are trying to validate is Int64 inside sql.NullInt64, the easiest way would be to remove the validate tag and just register a Struct Level validation using:
validate.RegisterStructValidation(NullInt64StructLevelValidation, NullInt64{})
while NullInt64StructLevelValidation is a StructLevelFunc that looks like this:
func NullInt64StructLevelValidation(sl validator.StructLevel) {
ni := sl.Current().Interface().(NullInt64)
if ni.NullInt64.Int64 < 10 {
sl.ReportError(ni.NullInt64.Int64, "Int64", "", "min", "")
}
}
Note #1: this line thing := Thing{number,&number,n,&n} has one argument too many. I assume you meant thing := Thing{number, n, &n}
Note #2: Go tooling including gofmt is considered to be one of the most powerful features of the language. Please consider using it/them.
EDIT #1:
I don't think implementing Valuer interface is of any value in this context.

How to check a log/output in go test?

I have this function that logs the error in some cases:
func readByte(/*...*/){
// ...
if err != nil {
fmt.Println("ERROR")
log.Print("Couldn't read first byte")
return
}
// ...
}
Now, in the test file, I want to check the output error from this function:
c.Assert(OUTPUT, check.Matches, "teste")
How can I access the log? I tried to put a buffer but it didn't work. What is the right way to catch this log without change my readByte function code?
For example,
readbyte_test.go:
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
"testing"
)
func readByte( /*...*/ ) {
// ...
err := io.EOF // force an error
if err != nil {
fmt.Println("ERROR")
log.Print("Couldn't read first byte")
return
}
// ...
}
func TestReadByte(t *testing.T) {
var buf bytes.Buffer
log.SetOutput(&buf)
defer func() {
log.SetOutput(os.Stderr)
}()
readByte()
t.Log(buf.String())
}
Output:
$ go test -v readbyte_test.go
=== RUN TestReadByte
ERROR
--- PASS: TestReadByte (0.00s)
readbyte_test.go:30: 2017/05/22 16:41:00 Couldn't read first byte
PASS
ok command-line-arguments 0.004s
$
Answer for Concurrent Tests
If your test is running concurrently (for example, when testing an http Server or Client), you may encounter a race between writing to the buffer and reading from it. Instead of the buffer, we can redirect output to an os.Pipe and use a bufio.Scanner to block until output has been written by using the Scan() method.
Here is an example of creating an os.Pipe and setting the stdlib log package to use the pipe. Note my use of the testify/assert package here:
func mockLogger(t *testing.T) (*bufio.Scanner, *os.File, *os.File) {
reader, writer, err := os.Pipe()
if err != nil {
assert.Fail(t, "couldn't get os Pipe: %v", err)
}
log.SetOutput(writer)
return bufio.NewScanner(reader), reader, writer
}
The *os.File objects are returned so they can be properly closed with a deferred function. Here I'm just printing to stdout since if there was some strange error on close I personally wouldn't want to fail the test. However, this could easily be another call to t.Errorf or similar if you wanted:
func resetLogger(reader *os.File, writer *os.File) {
err := reader.Close()
if err != nil {
fmt.Println("error closing reader was ", err)
}
if err = writer.Close(); err != nil {
fmt.Println("error closing writer was ", err)
}
log.SetOutput(os.Stderr)
}
And then in your test you would have this pattern:
scanner, reader, writer := mockLogger(t) // turn this off when debugging or developing as you will miss output!
defer resetLogger(reader, writer)
// other setup as needed, getting some value for thing below
go concurrentAction()
scanner.Scan() // blocks until a new line is written to the pipe
got := scanner.Text() // the last line written to the scanner
msg := fmt.Sprintf("your log message with thing %v you care about", thing)
assert.Contains(t, got, msg)
And finally, the concurrentAction() function is calling a log function (or method if using a log.logger, the package actually behaves the same way with log.SetOutput() call above either way) like:
// doing something, getting value for thing
log.Printf("your log message with the thing %v you care about", thing)

Accessing Data From Interfaces in Go

I am trying to implement a simple api in Golang. My experience in the backend is more with python and node, so I am having some difficulty printing out data held within the interface since it won't allow me to index it. I have searched around and several people have asked similar questions when the interface is one value, but not when the interface is a slice, I believe ([]interface{}). I have tried vaping the interface to no avail.
When I point the browser to /quandl/ddd/10 I would like to fmt.Println the specific numerical data, i.e. ("2017-01-13",
15.67,
16.41,
15.67,
16.11,
3595248,
0,
1,
15.67,
16.41,
15.67,
16.11,
3595248
])
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"github.com/fatih/color"
"github.com/gorilla/mux"
)
type QuandlResponse struct {
SourceCode string `json:"source_code"`
SourceName string `json:"source_name"`
Code string `json:"code"`
Frequency string `json:"frequency"`
FromDate string `json:"from_date"`
ToDate string `json:"to_date"`
Columns []string `json:"column_names"`
Data interface{} `json:"data"`
}
func getContent(w http.ResponseWriter, r *http.Request) {
stock := mux.Vars(r)["stock"]
limit := mux.Vars(r)["limit"]
url := "https://www.quandl.com/api/v1/datasets/WIKI/" + url.QueryEscape(stock) + ".json?&limit=" + url.QueryEscape(limit) + "&auth_token=XXXXX"
response, err := http.Get(url)
if err != nil {
fmt.Println(err)
}
contents, err := ioutil.ReadAll(response.Body)
var result QuandlResponse
json.Unmarshal(contents, &result)
json.NewEncoder(w).Encode(result)
fmt.Println(result.Data[0])
}
func callAll() {
rabbit := mux.NewRouter()
rabbit.HandleFunc("/quandl/{stock}/{limit}", getContent)
http.ListenAndServe(":8000", rabbit)
}
func main() {
color.Blue("Running Server #localhost:8000")
callAll()
}
If you know that the type of Data is []interface{}, you can do a type assertion:
slice := result.Data.([]interface{})
fmt.Println(slice[0])
If there are several possibilities for the type of Data, you can use a type switch:
switch data := result.Data.(type) {
case []interface{}:
fmt.Println(data[0])
case string:
fmt.Println(data)
default:
// unexpected type
}
You may also want to look at the reflect package if your requirements are more complicated.

How to fix line numbers in Go test output?

Let's consider this simple testing code.
(Note: assertSomething is super simple here, but normally I'd write a more specialised helper for the task at hand that would look at multiple things and could report more than one type of error.)
package hello
import "testing"
func TestFoo(t *testing.T) {
assertSomething(t, 2+2 == 4) // line 6
assertSomething(t, 2+3 == 6) // line 7
}
func assertSomething(t *testing.T, expected bool) {
if !expected {
t.Error("Something's not right") // line 12
}
}
When I run go test, I get the following:
--- FAIL: TestFoo (0.00s)
hello.go:12: Something's not right
FAIL
exit status 1
FAIL kos/hello 0.008s
I have two questions:
1) The error points to line 12 - why? How does t.Error find out which line it was called from?
2) In the helper, I'd like to specify that t.Error should look stack level higher to determine the line number to print, so that I would get a message like this:
--- FAIL: TestFoo (0.00s)
hello.go:7: Something's not right
Python allows me to do this, for instance, in warnings.warn("message", stacklevel=2) - how would I implement the equivalent here?
Things have changed since go 1.9.
Helper() method has been added to testing.T and testing.B. It's intended to be invoked from testing helpers such as assertSomething to indicate the function is a helper and we're not interested in line numbers coming from it.
package main
import "testing"
func TestFoo(t *testing.T) {
assertSomething(t, 2+2 == 4) // line 6
assertSomething(t, 2+3 == 6) // line 7
}
func assertSomething(t *testing.T, expected bool) {
if !expected {
t.Helper()
t.Error("Something's not right") // line 12
}
}
The output contains correct line numbers:
=== RUN TestFoo
--- FAIL: TestFoo (0.00s)
main.go:7: Something's not right
FAIL
You can also try it on Go Playground.
You can do what you're asking, and you can find out how t.Error works by looking at the source code. The function decorate is what you're looking for I think.
But, in the case where you have significant amounts of checking code, and for some reason it's getting duplicated in your test, it's better to extract that as a function that returns an error than passing in a testing.T and making it an "assertion". Indeed, writing assertion functions is explicitly discouraged in the language FAQ.
package hello
import "testing"
func TestFoo(t *testing.T) {
if err := checkSomething(2+2 == 4); err != nil {
t.Errorf("2+2=4 failed: %s", err)
}
if err := checkSomething(2+3 == 6); err != nil {
t.Errorf("2+3=6 failed: %s", err)
}
}
func checkSomething(v bool) error {
if !v {
return errors.New("something's not right")
}
return nil
}
But here's what I think idiomatic testing code would look like. It's table-driven, and the cases include inputs and expected output, leading to really clear error messages when the tests fail.
package hello
import "testing"
func TestFoo(t *testing.T) {
cases := []struct {
a, b, want int
}{
{2, 2, 4},
{2, 3, 6},
}
for _, c := range cases {
if got := operation(c.a, c.b); got != c.want {
t.Errorf("operation(%d, %d) = %d, want %d", c.a, c.b, got, c.want)
}
}
}
func operation(a, b int) int {
return a + b
}

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

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.
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.
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.
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.