Testing Stdout with go and ginkgo

6.6k Views Asked by At

here I am making my first steps in go trying to do BDD on a go command line app. I am using Ginkgo, which wraps testing.go and lets you do more expressive BDD. https://github.com/onsi/ginkgo

I am having issues in reading the stdout to do an assertion on it.

Found that on pkg/testing example do stub the output before running but I can not find the way to read that output: http://golang.org/src/pkg/testing/example.go

This is what I would like to do:

cli.go

 package cli
  
 import "fmt"
  
 func Run() {
        fmt.Println("Running cli")
 }

cli_test.go

package cli_test

import (
        . "github.com/altoros/bosh_deployer_cli/lib/cli"

        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
)

var _ = Describe("Cli", func() {
        It("should parse update stemcell flag", func() {
                Run()
                Expect(stdout).To(Equal("running cli"))
        })
})
2

There are 2 best solutions below

1
On

Testing Stdout can be tricky. You have multiple choice.

You can override os.Stdout during your test: (think to check the errors)

var _ = Describe("Cli", func() {
        It("should parse update stemcell flag", func() {
                r, w, _ := os.Pipe()
                tmp := os.Stdout
                defer func() {
                        os.Stdout = tmp
                }()
                os.Stdout = w
                go func() {
                        Run()
                        w.Close()
                }()
                stdout, _ := ioutil.ReadAll(r)
                Expect(string(stdout)).To(Equal("Running cli\n"))
        })
})

or you can pass a writer to your function:

cli.go

package cli

import (
        "fmt"
        "io"
)

func Run(w io.Writer) {
        fmt.Fprintln(w, "Running cli")
}

cli_test.go

package cli_test

import (
        . "cli"
        "io"
        "io/ioutil"

        . "github.com/onsi/ginkgo"
        . "github.com/onsi/gomega"
)

var _ = Describe("Cli", func() {
        It("should parse update stemcell flag", func() {
                r, w := io.Pipe()
                go func() {
                        Run(w)
                        w.Close()
                }()
                stdout, _ := ioutil.ReadAll(r)
                Expect(string(stdout)).To(Equal("Running cli\n"))
        })
})

main.go

package main

import (
        "cli"
        "os"
)

func main() {
        cli.Run(os.Stdout)
}
1
On

This is a classic usecase for dependency injection. You can use a gbytes.Buffer from Gomega and have something like this for a unit test:

var _ = Describe("Cli", func() {
        It("should parse update stemcell flag", func() {
                buffer := gbytes.NewBuffer()
                Run(buffer)
                Expect(buffer).To(gbytes.Say("Running cli\n"))
        })
})

Unless you're integration testing the cli, which case I'd recommend using gexec to gexec.Build the binary and then run gexec.Start the command, the resulting gexec.Session object will capture stdout and make it available as a gbytes.Buffer allowing you, again, to write:

Expect(session).To(gbytes.Say("Running cli\n")

More details on gbytes and gexec here:

http://onsi.github.io/gomega/#gbytes-testing-streaming-buffers

http://onsi.github.io/gomega/#gexec-testing-external-processes