fsmock

package module
v0.0.0-...-ea100f7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 7, 2022 License: Apache-2.0 Imports: 6 Imported by: 0

README

fsmock

A golang mock implementation of fs.FS and friends for testing.

CI Status Go Report Card Package Doc Releases

fsmock implements a mock filesystem satisfying fs.FS and other interfaces to enable easier testing of code that uses fs.FS to access file systems.

Installation

fsmock is provided as a go module and requires go >= 1.16.

go get github.com/halimath/fsmock@main

Usage

fsmock provides two basic types Dir and File. These can be used to build up a filesystem in plain go. Use the provided functions NewDir and NewFile to create them conveniently.

Create a new filesystem by invoking fsmock.New providing a root directory.

fsys := fsmock.New(fsmock.NewDir("",
    fsmock.EmptyFile("go.mod"),
    fsmock.EmptyFile("go.sum"),
    fsmock.NewDir("cmd",
        fsmock.TextFile("main.go", "package main"),
    ),
    fsmock.NewDir("internal",
        fsmock.EmptyFile("tool.go"),
        fsmock.EmptyFile("tool_test.go"),
    ),
))

You can use the methods defined in fs.FS and other interfaces from the fs package to access files and directories:

f, err := fsys.Open("cmd/main.go")
if err != nil {
    panic(err)
}
fs.WalkDir(fsys, "", func(path string, d fs.DirEntry, err error) error {
    // ...
    return nil
})

Modifying the filesystem

In addition to the read-only functions defined by the fs interfaces, fsmock provides some helper functions to modify the filesystem.

To update a modification timestamp of either a file or directory use the Touch function. This will also create an empty file if the named file does not exist (just like the Unix touch command does):

if err := fsys.Touch("internal/foo/foo.go"); err != nil {
    panic(err)
}

To create a directory use the Mkdir function which works like the mkdir Unix shell command (without the -p option):

if err := fsys.Mkdir("internal/foo"); err != nil {
    panic(err)
}

See fsmock_test.go for a full-blown example.

License

Copyright 2022 Alexander Metzner.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

Documentation

Overview

Package fsmock provides a testing facility to generate mock values that satisfy fs.FS and friends. The package supports developers writing readable, easy to maintain tests that work closely with the filesystem without relying to local or temporary filesystem operations.

Example
package main

import (
	"fmt"
	"io"
	"io/fs"

	"github.com/halimath/fsmock"
)

func main() {
	fsys := fsmock.New(fsmock.NewDir("",
		fsmock.EmptyFile("go.mod"),
		fsmock.EmptyFile("go.sum"),
		fsmock.NewDir("cmd",
			fsmock.TextFile("main.go", "package main"),
		),
		fsmock.NewDir("internal",
			fsmock.EmptyFile("tool.go"),
			fsmock.EmptyFile("tool_test.go"),
		),
	))

	f, err := fsys.Open("cmd/main.go")
	if err != nil {
		panic(err)
	}

	c, err := io.ReadAll(f)
	if err != nil {
		panic(err)
	}

	fmt.Println(string(c))

	fmt.Println("---")

	fs.WalkDir(fsys, "", func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			return err
		}

		if !d.IsDir() {
			fmt.Println(path)
		}

		return nil
	})

	fmt.Println("---")

	cmdTests, err := fs.Glob(fsys, "cmd/*_test.go")
	if err != nil {
		panic(err)
	}
	fmt.Println(len(cmdTests))

	if err := fsys.Touch("cmd/main_test.go"); err != nil {
		panic(err)
	}

	_, err = fsys.ReadFile("cmd/main_test.go")
	if err != nil {
		panic(err)
	}

	cmdTests, err = fs.Glob(fsys, "cmd/*_test.go")
	if err != nil {
		panic(err)
	}
	fmt.Println(len(cmdTests))

	fmt.Println("---")

	if err := fsys.Mkdir("internal/foo"); err != nil {
		panic(err)
	}

	if err := fsys.Touch("internal/foo/foo.go"); err != nil {
		panic(err)
	}

	fsub, err := fsys.Sub("internal/foo")
	if err != nil {
		panic(err)
	}

	if _, err := fsub.Open("foo.go"); err != nil {
		panic(err)
	}

}
Output:

package main
---
go.mod
go.sum
cmd/main.go
internal/tool.go
internal/tool_test.go
---
0
1
---

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Dir

type Dir struct {
	// Name defines the directory's name.
	Name string
	// ModTime defines the directory's modification time.
	ModTime time.Time
	// Children contains the directory's direct children.
	Children []Entry
}

Dir implements a directory in the filesystem.

func NewDir

func NewDir(name string, children ...Entry) *Dir

NewDir creates a new directory.

type Entry

type Entry interface {
	// contains filtered or unexported methods
}

Entry is an internal interface defining entries in a filesystem's directory. Dir and File both satisfy this internal interface and can be used in client code.

type FS

type FS struct {
	// contains filtered or unexported fields
}

FS implements a mocked fs.FS. It also implements several other interfaces from the fs package, namely - fs.ReadDirFS - fs.ReadFileFS - fs.StatFS - fs.SubFS

In addition FS provides some useful methods to create files, directories and modify existing ones which reflect some of the basic POSIX shell commands, such as mkdir, rm, touch, ...

func New

func New(root *Dir) *FS

New creates a new FS using root as the root directory.

func (*FS) Mkdir

func (f *FS) Mkdir(name string) error

Mkdir works like the POSIX shell command mkdir and creates the directory name. All parent directories must exist for this call to succeed (it does not work like mkdir -p on some systems).

func (*FS) Open

func (f *FS) Open(name string) (fs.File, error)

Open opens the named file.

When Open returns an error, it should be of type *PathError with the Op field set to "open", the Path field set to name, and the Err field describing the problem.

Open should reject attempts to open names that do not satisfy ValidPath(name), returning a *PathError with Err set to ErrInvalid or ErrNotExist.

func (*FS) ReadDir

func (f *FS) ReadDir(name string) ([]fs.DirEntry, error)

ReadDir reads the named directory and returns a list of directory entries sorted by filename.

func (*FS) ReadFile

func (f *FS) ReadFile(name string) ([]byte, error)

ReadFile reads the named file and returns its contents. A successful call returns a nil error, not io.EOF. (Because ReadFile reads the whole file, the expected EOF from the final Read is not treated as an error to be reported.)

The caller is permitted to modify the returned byte slice. This method should return a copy of the underlying data.

func (*FS) Stat

func (f *FS) Stat(name string) (fs.FileInfo, error)

Stat returns a FileInfo describing the file. If there is an error, it should be of type *PathError.

func (*FS) Sub

func (f *FS) Sub(name string) (fs.FS, error)

Sub returns an FS corresponding to the subtree rooted at dir.

func (*FS) Touch

func (f *FS) Touch(name string) error

Touch works like the POSIX shell command touch. It either updates the modified timestamp of the file or directory named name or creates an empty file with that name if name's parent exists and is a directory.

type File

type File struct {
	// Name defines the file's base name.
	Name string
	// Mode defines the file's mode.
	Mode fs.FileMode
	// ModTime defines the file's modification time.
	ModTime time.Time
	// Content is the file's content.
	Content []byte
}

File is a file in the fileysystem.

func EmptyFile

func EmptyFile(name string) *File

EmptyFile creates an empty file.

func NewFile

func NewFile(name string, content []byte) *File

NewFile creates a new file.

func TextFile

func TextFile(name, content string) *File

TextFile creates a new file with string content.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL