feat(options): add optional Options arguments to New() function (#14)

Enables simpler creation of custom *Golden instances, as you can just pass in the custom values to New(), rather than modifying fields after increating the Golden instance.
This commit is contained in:
2025-04-05 12:28:57 +01:00
committed by GitHub
parent 8a88e5174b
commit 550ba17fb0
4 changed files with 254 additions and 51 deletions

View File

@@ -257,14 +257,21 @@ type Golden struct {
}
// New returns a new *Golden instance with default values correctly populated.
func New() *Golden {
return &Golden{
// It accepts zero or more Option functions that can modify the default values.
func New(opts ...Option) *Golden {
g := &Golden{
DirMode: DefaultDirMode,
FileMode: DefaultFileMode,
Suffix: DefaultSuffix,
Dirname: DefaultDirname,
UpdateFunc: DefaultUpdateFunc,
}
for _, opt := range opts {
opt(g)
}
return g
}
// Do is a convenience function for calling Update(), Set(), and Get() in a

View File

@@ -11,6 +11,37 @@ import (
"github.com/stretchr/testify/require"
)
// assertSameFunc asserts that two functions are the same by comparing their
// function names via reflection.
func assertSameFunc(t *testing.T, want, got interface{}) {
t.Helper()
// Verify both arguments are functions
wantType := reflect.TypeOf(want)
gotType := reflect.TypeOf(got)
if wantType.Kind() != reflect.Func {
t.Fatalf(
"assertSameFunc: 'want' argument is not a function, got %s",
wantType.Kind(),
)
}
if gotType.Kind() != reflect.Func {
t.Fatalf(
"assertSameFunc: 'got' argument is not a function, got %s",
gotType.Kind(),
)
}
gotFP := reflect.ValueOf(got).Pointer()
gotFuncName := runtime.FuncForPC(gotFP).Name()
wantFP := reflect.ValueOf(want).Pointer()
wantFuncName := runtime.FuncForPC(wantFP).Name()
assert.Equal(t, wantFuncName, gotFuncName)
}
func TestDefaults(t *testing.T) {
t.Run("Default", func(t *testing.T) {
assert.IsType(t, &Golden{}, Default)
@@ -19,15 +50,7 @@ func TestDefaults(t *testing.T) {
assert.Equal(t, DefaultFileMode, Default.FileMode)
assert.Equal(t, DefaultSuffix, Default.Suffix)
assert.Equal(t, DefaultDirname, Default.Dirname)
// Use runtime.FuncForPC() to verify the UpdateFunc value is set to
// the EnvUpdateFunc function by default.
gotFP := reflect.ValueOf(Default.UpdateFunc).Pointer()
gotFuncName := runtime.FuncForPC(gotFP).Name()
wantFP := reflect.ValueOf(EnvUpdateFunc).Pointer()
wantFuncName := runtime.FuncForPC(wantFP).Name()
assert.Equal(t, wantFuncName, gotFuncName)
assertSameFunc(t, EnvUpdateFunc, Default.UpdateFunc)
})
t.Run("DefaultDirMode", func(t *testing.T) {
@@ -47,17 +70,10 @@ func TestDefaults(t *testing.T) {
})
t.Run("DefaultUpdateFunc", func(t *testing.T) {
gotFP := reflect.ValueOf(DefaultUpdateFunc).Pointer()
gotFuncName := runtime.FuncForPC(gotFP).Name()
wantFP := reflect.ValueOf(EnvUpdateFunc).Pointer()
wantFuncName := runtime.FuncForPC(wantFP).Name()
assert.Equal(t, wantFuncName, gotFuncName)
assertSameFunc(t, EnvUpdateFunc, DefaultUpdateFunc)
})
}
// TestNew is a horribly hack to test that the New() function uses the
// package-level Default* variables.
func TestNew(t *testing.T) {
t.Run("customized Default* variables", func(t *testing.T) {
// Capture the default values before we change them.
defaultDirMode := DefaultDirMode
defaultFileMode := DefaultFileMode
@@ -90,14 +106,93 @@ func TestNew(t *testing.T) {
assert.Equal(t, DefaultFileMode, got.FileMode)
assert.Equal(t, DefaultSuffix, got.Suffix)
assert.Equal(t, DefaultDirname, got.Dirname)
assertSameFunc(t, updateFunc, got.UpdateFunc)
})
}
// Verify the UpdateFunc value is set to the new value.
gotFP := reflect.ValueOf(got.UpdateFunc).Pointer()
gotFuncName := runtime.FuncForPC(gotFP).Name()
wantFP := reflect.ValueOf(updateFunc).Pointer()
wantFuncName := runtime.FuncForPC(wantFP).Name()
func TestNew(t *testing.T) {
t.Run("defaults (no options)", func(t *testing.T) {
g := New()
assert.Equal(t, DefaultDirMode, g.DirMode)
assert.Equal(t, DefaultFileMode, g.FileMode)
assert.Equal(t, DefaultSuffix, g.Suffix)
assert.Equal(t, DefaultDirname, g.Dirname)
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
})
assert.Equal(t, wantFuncName, gotFuncName)
// Test each option individually
t.Run("WithDirMode", func(t *testing.T) {
customMode := os.FileMode(0o700)
g := New(WithDirMode(customMode))
assert.Equal(t, customMode, g.DirMode)
assert.Equal(t, DefaultFileMode, g.FileMode)
assert.Equal(t, DefaultSuffix, g.Suffix)
assert.Equal(t, DefaultDirname, g.Dirname)
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
})
t.Run("WithFileMode", func(t *testing.T) {
customMode := os.FileMode(0o600)
g := New(WithFileMode(customMode))
assert.Equal(t, DefaultDirMode, g.DirMode)
assert.Equal(t, customMode, g.FileMode)
assert.Equal(t, DefaultSuffix, g.Suffix)
assert.Equal(t, DefaultDirname, g.Dirname)
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
})
t.Run("WithSuffix", func(t *testing.T) {
customSuffix := ".my-suffix"
g := New(WithSuffix(customSuffix))
assert.Equal(t, DefaultDirMode, g.DirMode)
assert.Equal(t, DefaultFileMode, g.FileMode)
assert.Equal(t, customSuffix, g.Suffix)
assert.Equal(t, DefaultDirname, g.Dirname)
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
})
t.Run("WithDirname", func(t *testing.T) {
customDirname := "fixtures/generated"
g := New(WithDirname(customDirname))
assert.Equal(t, DefaultDirMode, g.DirMode)
assert.Equal(t, DefaultFileMode, g.FileMode)
assert.Equal(t, DefaultSuffix, g.Suffix)
assert.Equal(t, customDirname, g.Dirname)
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
})
t.Run("WithUpdateFunc", func(t *testing.T) {
customUpdateFunc := func() bool { return true }
g := New(WithUpdateFunc(customUpdateFunc))
assert.Equal(t, DefaultDirMode, g.DirMode)
assert.Equal(t, DefaultFileMode, g.FileMode)
assert.Equal(t, DefaultSuffix, g.Suffix)
assert.Equal(t, DefaultDirname, g.Dirname)
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
})
// Test multiple options at once
t.Run("MultipleOptions", func(t *testing.T) {
customDirMode := os.FileMode(0o700)
customFileMode := os.FileMode(0o600)
customSuffix := ".fixture"
customDirname := "fixtures"
customUpdateFunc := func() bool { return true }
g := New(
WithDirMode(customDirMode),
WithFileMode(customFileMode),
WithSuffix(customSuffix),
WithDirname(customDirname),
WithUpdateFunc(customUpdateFunc),
)
assert.Equal(t, customDirMode, g.DirMode)
assert.Equal(t, customFileMode, g.FileMode)
assert.Equal(t, customSuffix, g.Suffix)
assert.Equal(t, customDirname, g.Dirname)
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
})
}
func TestDo(t *testing.T) {

43
options.go Normal file
View File

@@ -0,0 +1,43 @@
package golden
import (
"os"
)
// Option is a function that modifies a Golden instance.
type Option func(*Golden)
// WithDirMode sets the directory mode for a Golden instance.
func WithDirMode(mode os.FileMode) Option {
return func(g *Golden) {
g.DirMode = mode
}
}
// WithFileMode sets the file mode for a Golden instance.
func WithFileMode(mode os.FileMode) Option {
return func(g *Golden) {
g.FileMode = mode
}
}
// WithSuffix sets the file suffix for a Golden instance.
func WithSuffix(suffix string) Option {
return func(g *Golden) {
g.Suffix = suffix
}
}
// WithDirname sets the directory name for a Golden instance.
func WithDirname(dirname string) Option {
return func(g *Golden) {
g.Dirname = dirname
}
}
// WithUpdateFunc sets the update function for a Golden instance.
func WithUpdateFunc(updateFunc UpdateFunc) Option {
return func(g *Golden) {
g.UpdateFunc = updateFunc
}
}

58
options_test.go Normal file
View File

@@ -0,0 +1,58 @@
package golden
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestWithDirMode(t *testing.T) {
customMode := os.FileMode(0o700)
g := &Golden{}
opt := WithDirMode(customMode)
opt(g)
assert.Equal(t, customMode, g.DirMode)
}
func TestWithFileMode(t *testing.T) {
customMode := os.FileMode(0o600)
g := &Golden{}
opt := WithFileMode(customMode)
opt(g)
assert.Equal(t, customMode, g.FileMode)
}
func TestWithSuffix(t *testing.T) {
customSuffix := ".custom"
g := &Golden{}
opt := WithSuffix(customSuffix)
opt(g)
assert.Equal(t, customSuffix, g.Suffix)
}
func TestWithDirname(t *testing.T) {
customDirname := "custom-testdata"
g := &Golden{}
opt := WithDirname(customDirname)
opt(g)
assert.Equal(t, customDirname, g.Dirname)
}
func TestWithUpdateFunc(t *testing.T) {
customUpdateFunc := func() bool { return true }
g := &Golden{}
opt := WithUpdateFunc(customUpdateFunc)
opt(g)
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
}