Skip to content

Commit

Permalink
Allow one-shot configuration options (#34)
Browse files Browse the repository at this point in the history
* s/coverage.txt/coverage.out/

* Remove Snapshotter interface, make Config exported, support one-shot configuration

* Add WithOptions test
  • Loading branch information
bradleyjkemp authored May 19, 2018
1 parent 28343ef commit 6beff3c
Show file tree
Hide file tree
Showing 7 changed files with 53 additions and 44 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ script:
- make test-ci

after_success:
- $GOPATH/bin/goveralls -service=travis-ci -coverprofile=coverage.txt
- $GOPATH/bin/goveralls -service=travis-ci -coverprofile=coverage.out

notifications:
email:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test-ci: coverage lint

.PHONY: coverage
coverage:
go test -v -coverpkg ./... -coverprofile coverage.txt ./...
go test -v -coverpkg ./... -coverprofile coverage.out ./...

.PHONY: clean
clean:
Expand Down
28 changes: 18 additions & 10 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package cupaloy

// Configurator is a functional option that can be passed to cupaloy.New() to change snapshotting behaviour.
type Configurator func(*config)
type Configurator func(*Config)

// EnvVariableName can be used to customize the environment variable that determines whether snapshots should be updated
// e.g.
// cupaloy.New(EnvVariableName("UPDATE"))
// Will create an instance where snapshots will be updated if the UPDATE environment variable is set,
// instead of the default of UPDATE_SNAPSHOTS.
func EnvVariableName(name string) Configurator {
return func(c *config) {
return func(c *Config) {
c.shouldUpdate = func() bool {
return envVariableSet(name)
}
Expand All @@ -22,7 +22,7 @@ func EnvVariableName(name string) Configurator {
// cupaloy.New(ShouldUpdate(func () bool { return *update })
// Will create an instance where snapshots are updated if the --update flag is passed to go test.
func ShouldUpdate(f func() bool) Configurator {
return func(c *config) {
return func(c *Config) {
c.shouldUpdate = f
}
}
Expand All @@ -32,19 +32,27 @@ func ShouldUpdate(f func() bool) Configurator {
// cupaloy.New(SnapshotSubdirectory("testdata"))
// Will create an instance where snapshots are stored in testdata/ rather than the default .snapshots/
func SnapshotSubdirectory(name string) Configurator {
return func(c *config) {
return func(c *Config) {
c.subDirName = name
}
}

type config struct {
// Config provides the same snapshotting functions with additional configuration capabilities.
type Config struct {
shouldUpdate func() bool
subDirName string
}

func defaultConfig() *config {
c := &config{}
SnapshotSubdirectory(".snapshots")(c)
EnvVariableName("UPDATE_SNAPSHOTS")(c)
return c
func defaultConfig() *Config {
return (&Config{}).WithOptions(
SnapshotSubdirectory(".snapshots"),
EnvVariableName("UPDATE_SNAPSHOTS"),
)
}

func (c *Config) clone() *Config {
return &Config{
shouldUpdate: c.shouldUpdate,
subDirName: c.subDirName,
}
}
56 changes: 27 additions & 29 deletions cupaloy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,9 @@ import (
"testing"
)

// Snapshotter is the API for taking snapshots of values in your tests.
type Snapshotter interface {
// Snapshot compares the given value to the it's previous value stored on the filesystem.
// An error containing a diff is returned if the snapshots do not match.
// Snapshot determines the snapshot file automatically from the name of the calling function.
Snapshot(i ...interface{}) error

// SnapshotMulti is identical to Snapshot but can be called multiple times from the same function.
// This is done by providing a unique snapshotId for each invocation.
SnapshotMulti(snapshotID string, i ...interface{}) error

// SnapshotT is identical to Snapshot but gets the snapshot name using
// t.Name() and calls t.Fail() directly if the snapshots do not match.
SnapshotT(t *testing.T, i ...interface{})
}

// New constructs a new, configured instance of cupaloy using the given Configurators.
func New(configurators ...Configurator) Snapshotter {
config := defaultConfig()

for _, configurator := range configurators {
configurator(config)
}

return config
func New(configurators ...Configurator) *Config {
return defaultConfig().WithOptions(configurators...)
}

// Snapshot calls Snapshotter.Snapshot with the default config.
Expand All @@ -51,29 +29,49 @@ func SnapshotT(t *testing.T, i ...interface{}) {
defaultConfig().SnapshotT(t, i...)
}

func (c *config) Snapshot(i ...interface{}) error {
// Snapshot compares the given value to the it's previous value stored on the filesystem.
// An error containing a diff is returned if the snapshots do not match.
// Snapshot determines the snapshot file automatically from the name of the calling function.
func (c *Config) Snapshot(i ...interface{}) error {
return c.snapshot(getNameOfCaller(), i...)
}

func (c *config) SnapshotMulti(snapshotID string, i ...interface{}) error {
// SnapshotMulti is identical to Snapshot but can be called multiple times from the same function.
// This is done by providing a unique snapshotId for each invocation.
func (c *Config) SnapshotMulti(snapshotID string, i ...interface{}) error {
snapshotName := fmt.Sprintf("%s-%s", getNameOfCaller(), snapshotID)
return c.snapshot(snapshotName, i...)
}

func (c *config) SnapshotT(t *testing.T, i ...interface{}) {
// SnapshotT is identical to Snapshot but gets the snapshot name using
// t.Name() and calls t.Fail() directly if the snapshots do not match.
func (c *Config) SnapshotT(t *testing.T, i ...interface{}) {
t.Helper()
if t.Failed() {
return
}

snapshotName := strings.Replace(t.Name(), "/", "-", -1)
err := c.snapshot(snapshotName, i...)
if err != nil {
t.Error(err)
}
}

func (c *config) snapshot(snapshotName string, i ...interface{}) error {
// WithOptions allows the modification of an existing Config. This can usefully be
// used to use a different option for a single call e.g.
// snapshotter.WithOptions(cupaloy.SnapshotSubdirectory("testdata")).SnapshotT(t, result)
func (c *Config) WithOptions(configurators ...Configurator) *Config {
clonedConfig := c.clone()

for _, configurator := range configurators {
configurator(clonedConfig)
}

return clonedConfig
}

func (c *Config) snapshot(snapshotName string, i ...interface{}) error {
snapshot := takeSnapshot(i...)

prevSnapshot, err := c.readSnapshot(snapshotName)
Expand Down
2 changes: 2 additions & 0 deletions examples/advanced_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ func TestConfig(t *testing.T) {
if err != nil {
t.Fatalf("The config struct has all the same methods as the default %s", err)
}

snapshotter.WithOptions(cupaloy.SnapshotSubdirectory("testdata")).SnapshotT(t, "Hello world!")
}

// If a snapshot is updated then this returns an error
Expand Down
1 change: 1 addition & 0 deletions examples/testdata/TestConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello world!
6 changes: 3 additions & 3 deletions util.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func envVariableSet(envVariable string) bool {
return varSet
}

func (c *config) snapshotFilePath(testName string) string {
func (c *Config) snapshotFilePath(testName string) string {
return filepath.Join(c.subDirName, testName)
}

Expand Down Expand Up @@ -63,7 +63,7 @@ func takeSnapshot(i ...interface{}) string {
return snapshot.String()
}

func (c *config) readSnapshot(snapshotName string) (string, error) {
func (c *Config) readSnapshot(snapshotName string) (string, error) {
snapshotFile := c.snapshotFilePath(snapshotName)
buf, err := ioutil.ReadFile(snapshotFile)

Expand All @@ -78,7 +78,7 @@ func (c *config) readSnapshot(snapshotName string) (string, error) {
return string(buf), nil
}

func (c *config) updateSnapshot(snapshotName string, snapshot string) error {
func (c *Config) updateSnapshot(snapshotName string, snapshot string) error {
// check that subdirectory exists before writing snapshot
err := os.MkdirAll(c.subDirName, os.ModePerm)
if err != nil {
Expand Down

0 comments on commit 6beff3c

Please sign in to comment.