模拟bufio.NewScanner的标准输出流

I want to write a test for a utility function where I use bufio.NewScanner and Scan(). I usually use it on stdout and now I want to simulate a short piece of stream where I can return some static string for the purpose of the test.

bufio.NewScanner(r io.Reader) takes a Reader but that only requires a read method. By reading the source code I couldn't figure out from which buffer it reads or how that gets passed in.

How can I mock that in a short and concise way?

To simply test your code you can use @Sven's answer.

To get an idea of a simple io.Reader for testing, consider below example:

type R struct {
    Data string
    done bool
}

func (r *R) Read(p []byte) (n int, err error) {
    copy(p, []byte(r.Data))
    if r.done {
        return 0, io.EOF
    }
    r.done = true
    return len([]byte(r.Data)), nil
}

R is a custom test type that's implementing an io.Reader interface by having a Read method. Thus, it will be accepted by NewScanner. It can be used as:

func NewR(data string) *R {
    return &R{data, false}
}

r := NewR("Test
message
")
scanner := bufio.NewScanner(r)

for scanner.Scan() {
    fmt.Printf("Line: %s
", scanner.Text())
}

Output:

Line: Test 
Line: message

Both the type R and its Read method have been defined to work as a simple source of bytes. Scan calls its reader's (input to NewScanner) Read method until it gets an EOF or an error. For simplicity, R's Read method copies its data to caller's buffer (p) in its first call and returns an EOF for any subsequent calls.

Note that actual splitting of the lines at is done by scanner.Scan and not r.Read.

You can modify the Read method above to get custom behavior as per your requirements.

Working example: https://play.golang.org/p/zqDoQDIE93

You can use bytes.Buffer since it implements the io.Reader functions.

Example https://play.golang.org/p/gjjMmT3SzD:

package main

import (
    "bufio"
    "bytes"
    "fmt"
)

func main() {
    buf := bytes.NewBufferString("foo
bar")
    scanner := bufio.NewScanner(buf)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}