Package vfs
provides an abstraction of the os
and io
packages that is easy
to test.
-
File system abstraction layer for commonly-used
os
andio
functions from the standard library. -
Powerful and easy-to-use declarative testing framework,
vfst
. You declare the desired state of the filesystem after your code has run, andvfst
tests that the filesystem matches that state. For a quick tour ofvfst
's features, see the examples in the documentation. -
Compatibility with
github.com/spf13/afero
andgit.luolix.top/src-d/go-billy
.
vfs
provides implementations of the FS
interface:
// An FS is an abstraction over commonly-used functions in the os and io
// packages.
type FS interface {
Chmod(name string, mode fs.FileMode) error
Chown(name string, uid, git int) error
Chtimes(name string, atime, mtime time.Time) error
Create(name string) (*os.File, error)
Glob(pattern string) ([]string, error)
Lchown(name string, uid, git int) error
Link(oldname, newname string) error
Lstat(name string) (fs.FileInfo, error)
Mkdir(name string, perm fs.FileMode) error
Open(name string) (fs.File, error)
OpenFile(name string, flag int, perm fs.FileMode) (*os.File, error)
PathSeparator() rune
RawPath(name string) (string, error)
ReadDir(dirname string) ([]fs.DirEntry, error)
ReadFile(filename string) ([]byte, error)
Readlink(name string) (string, error)
Remove(name string) error
RemoveAll(name string) error
Rename(oldpath, newpath string) error
Stat(name string) (fs.FileInfo, error)
Symlink(oldname, newname string) error
Truncate(name string, size int64) error
WriteFile(filename string, data []byte, perm fs.FileMode) error
}
To use vfs
, you write your code to use the FS
interface, and then use
vfst
to test it.
vfs
also provides functions MkdirAll
(equivalent to os.MkdirAll
),
Contains
(an improved filepath.HasPrefix
), and Walk
(equivalent to
filepath.Walk
) that operate on an FS
.
The implementations of FS
provided are:
-
OSFS
which calls the underlyingos
andio
functions directly. -
PathFS
which transforms all paths to provide a poor-man'schroot
. -
ReadOnlyFS
which prevents modification of the underlying FS. -
TestFS
which assists running tests on a real filesystem but in a temporary directory that is easily cleaned up. It usesOSFS
under the hood.
Example usage:
// writeConfigFile is the function we're going to test. It can make arbitrary
// changes to the filesystem through fileSystem.
func writeConfigFile(fileSystem vfs.FS) error {
return fileSystem.WriteFile("/home/user/app.conf", []byte(`app config`), 0644)
}
// TestWriteConfigFile is our test function.
func TestWriteConfigFile(t *testing.T) {
// Create and populate an temporary directory with a home directory.
fileSystem, cleanup, err := vfst.NewTestFS(map[string]any{
"/home/user/.bashrc": "# contents of user's .bashrc\n",
})
// Check that the directory was populated successfully.
if err != nil {
t.Fatalf("vfsTest.NewTestFS(_) == _, _, %v, want _, _, <nil>", err)
}
// Ensure that the temporary directory is removed.
defer cleanup()
// Call the function we want to test.
if err := writeConfigFile(fileSystem); err != nil {
t.Error(err)
}
// Check properties of the filesystem after our function has modified it.
vfst.RunTests(t, fileSystem, "app_conf",
vfst.TestPath("/home/user/app.conf",
vfst.TestModeIsRegular(),
vfst.TestModePerm(0644),
vfst.TestContentsString("app config"),
),
)
}
There is a compatibility shim for
github.com/spf13/afero
in
github.com/twpayne/go-vfsafero
. This
allows you to use vfst
to test existing code that uses
afero.FS
. See the
documentation for an example.
There is a compatibility shim for
github.com/src-d/go-billy
in
github.com/twpayne/go-vfsbilly
. This
allows you to use vfst
to test existing code that uses
billy.Filesystem
.
See the documentation for an
example.
vfs
was inspired by
github.com/spf13/afero
. So, why not use
afero
?
-
afero
has several critical bugs in its in-memory mock filesystem implementationMemMapFs
, to the point that it is unusable for non-trivial test cases.vfs
does not attempt to implement an in-memory mock filesystem, and instead only provides a thin layer around the standard library'sos
andio
packages, and as such should have fewer bugs. -
afero
does not support creating or reading symbolic links, and itsLstatIfPossible
interface is clumsy to use as it is not part of theafero.Fs
interface.vfs
provides out-of-the-box support for symbolic links with all methods in theFS
interface. -
afero
has been effectively abandoned by its author, and a "friendly fork" (github.com/absfs/afero
) has not seen much activity.vfs
, by providing much less functionality thanafero
, should be smaller and easier to maintain.
MIT