In-memory file for testing

As mentioned in "Pragmatic and Effective Testing in Go", you can use spf13/afero for file abstraction which leads to easier tests.

That library has a Using Afero for Testing:

There is a large benefit to using a mock filesystem for testing.

It has a completely blank state every time it is initialized and can be easily reproducible regardless of OS.
You could create files to your heart’s content and the file access would be fast while also saving you from all the annoying issues with deleting temporary files, Windows file locking, etc.
The MemMapFs backend is perfect for testing.

  • Much faster than performing I/O operations on disk
  • Avoid security issues and permissions
  • Far more control. 'rm -rf /' with confidence
  • Test setup is far more easier to do
  • No test cleanup needed

It uses a fully atomic memory backed filesystem (MemMapFs).
It is fully concurrent and will work within go routines safely.


You can use a Buffer.

In general, it's a good idea to use io.Reader and io.Writer interfaces in your code (Buffer implements both) to deal with IO. That way you can deal with various methods of input/output (local file, memory buffer, network connection...) in the same way, without knowing what you are dealing with in the specific function you're using. It makes it more abstract and makes testing trivial.


Example of use with a trivial function:

Function definition:

// mypkg project mypkg.go
package mypkg

import (
    "bufio"
    "io"
    "strings"
)

func MyFunction(in io.Reader, out io.Writer) {
    rd := bufio.NewReader(in)
    str, _ := rd.ReadString('\n')
    io.WriteString(out, strings.TrimSuffix(str, "\n")+" was input\n")
}

Function use within a program:

package main

import (
    "mypkg"
    "os"
)

func main() {
    mypkg.MyFunction(os.Stdin, os.Stdout)
}

Test:

// mypkg project mypkg_test.go
package mypkg

import (
    "bytes"
    "testing"
)

func TestMyFunction(t *testing.T) {
    ibuf := bytes.NewBufferString("hello\n")
    obuf := bytes.NewBufferString("")
    MyFunction(ibuf, obuf)
    if obuf.String() != "hello was input\n" {
        t.Fail()
    }
}

If you need an io.ReadSeeker, and you don't need write access, use bytes.Reader:

import "bytes"

data := []byte("success")
readSeeker := bytes.NewReader(data)

This is useful for things like http.ServeContent().