C0d3r42
C0d3r42

Reputation: 75

How to discard printouts when running tests

Using Go 1.11 I have a function that prints a label above a user input. This is fine and works but when I have started to write the test for this function it prints out this label when running the tests.

I have also use log.Print but then in the test file adding the following

func init() {
  log.SetOutput(ioutil.Discard)
}

This stops the log.Print from being displayed when running the tests. So how do I get it to do the same for any fmt.Println?

UPDATE

I thought I would post the func that I am testing and how I set up,

func checkLoop(loopCount int) int {
  var input int
  var ok bool

  for !ok {
    fmt.Printf("Input %d  ::  ", loopCount)
    ok, input = takeInput()
  }

  return input
}

So takeInput() only takes the users input, using fmt.Scan and checks it to see if its within a set range I want. So it returns a bool and if its false it will re-use the label to check what input number it is.

Upvotes: 5

Views: 1871

Answers (3)

peterSO
peterSO

Reputation: 166855

For example,

discard_test.go:

package main

import (
    "fmt"
    "os"
    "syscall"
    "testing"
)

func printLabel(label string) {
    fmt.Println(label)
}

func TestDiscardLabel(t *testing.T) {
    defer func(stdout *os.File) {
        os.Stdout = stdout
    }(os.Stdout)
    os.Stdout = os.NewFile(uintptr(syscall.Stdin), os.DevNull)
    printLabel("Discard:")
}

func TestPrintLabel(t *testing.T) {
    printLabel("Print:")
}

Output:

$ go test discard_test.go -v
--- PASS: TestDiscardLabel (0.00s)
=== RUN   TestPrintLabel
Print:
--- PASS: TestPrintLabel (0.00s)
PASS
$ 

Upvotes: 3

Matteo
Matteo

Reputation: 39430

fmt.Printf use the os.Stdout so you can simply:

os.Stdout = nil
fmt.Println("Hello Gopher!")

or more elegant:

os.Stdout,_ = os.Open(os.DevNull)
fmt.Println("Hello Gopher!")

Hope this help

Upvotes: 1

kostix
kostix

Reputation: 55563

Either you're testing on a wrong level or you're testing a wrong thing, let me explain.

  • If it's for some reason valuable for you to test that your function prints something and also accepts user input, then go ahead and do end-to-end testing for it—that is, also test that it prints what it's expected to print, and once it does that, submit it some canned input and then verify it processes it the way you expect it to.

    I don't know your code but supposedly you should stop using fmt.P* family of functions—which imply using os.Std{in|out|err} and write the functions forming the core of your code accepting io.Reader and io.Writer interfaces and call them from the top-level code passing them os.Stdin and os.Stderr etc.

    Then, when testing, it will be easy to pass these functions stub (or mock) values which also satisfy the said interfaces.

    (The fmt package supports formatted printing using the fmt.FP* family of functions, where that F is a historical artefact meaning "file" and allows you to pass it any value implementing io.Writer.)

  • Another possibility is that what you explained looks like a code smell: a function concerned with processing some input data has no business printing any labels anywhere.

    So it well may be that you instead should split your function at least in two: the "outer" one prompts the user and reads the data, and the second accepts the data and processes it. You then test the second one.

Upvotes: 3

Related Questions