Skip to content

Commit 3c930b0

Browse files
committed
feat(options): add optional Options arguments to New() function
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.
1 parent 8a88e51 commit 3c930b0

File tree

4 files changed

+254
-51
lines changed

4 files changed

+254
-51
lines changed

golden.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,21 @@ type Golden struct {
257257
}
258258

259259
// New returns a new *Golden instance with default values correctly populated.
260-
func New() *Golden {
261-
return &Golden{
260+
// It accepts zero or more Option functions that can modify the default values.
261+
func New(opts ...Option) *Golden {
262+
g := &Golden{
262263
DirMode: DefaultDirMode,
263264
FileMode: DefaultFileMode,
264265
Suffix: DefaultSuffix,
265266
Dirname: DefaultDirname,
266267
UpdateFunc: DefaultUpdateFunc,
267268
}
269+
270+
for _, opt := range opts {
271+
opt(g)
272+
}
273+
274+
return g
268275
}
269276

270277
// Do is a convenience function for calling Update(), Set(), and Get() in a

golden_test.go

Lines changed: 144 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,37 @@ import (
1111
"github.com/stretchr/testify/require"
1212
)
1313

14+
// assertSameFunc asserts that two functions are the same by comparing their
15+
// function names via reflection.
16+
func assertSameFunc(t *testing.T, want, got interface{}) {
17+
t.Helper()
18+
19+
// Verify both arguments are functions
20+
wantType := reflect.TypeOf(want)
21+
gotType := reflect.TypeOf(got)
22+
23+
if wantType.Kind() != reflect.Func {
24+
t.Fatalf(
25+
"assertSameFunc: 'want' argument is not a function, got %s",
26+
wantType.Kind(),
27+
)
28+
}
29+
30+
if gotType.Kind() != reflect.Func {
31+
t.Fatalf(
32+
"assertSameFunc: 'got' argument is not a function, got %s",
33+
gotType.Kind(),
34+
)
35+
}
36+
37+
gotFP := reflect.ValueOf(got).Pointer()
38+
gotFuncName := runtime.FuncForPC(gotFP).Name()
39+
wantFP := reflect.ValueOf(want).Pointer()
40+
wantFuncName := runtime.FuncForPC(wantFP).Name()
41+
42+
assert.Equal(t, wantFuncName, gotFuncName)
43+
}
44+
1445
func TestDefaults(t *testing.T) {
1546
t.Run("Default", func(t *testing.T) {
1647
assert.IsType(t, &Golden{}, Default)
@@ -19,15 +50,7 @@ func TestDefaults(t *testing.T) {
1950
assert.Equal(t, DefaultFileMode, Default.FileMode)
2051
assert.Equal(t, DefaultSuffix, Default.Suffix)
2152
assert.Equal(t, DefaultDirname, Default.Dirname)
22-
23-
// Use runtime.FuncForPC() to verify the UpdateFunc value is set to
24-
// the EnvUpdateFunc function by default.
25-
gotFP := reflect.ValueOf(Default.UpdateFunc).Pointer()
26-
gotFuncName := runtime.FuncForPC(gotFP).Name()
27-
wantFP := reflect.ValueOf(EnvUpdateFunc).Pointer()
28-
wantFuncName := runtime.FuncForPC(wantFP).Name()
29-
30-
assert.Equal(t, wantFuncName, gotFuncName)
53+
assertSameFunc(t, EnvUpdateFunc, Default.UpdateFunc)
3154
})
3255

3356
t.Run("DefaultDirMode", func(t *testing.T) {
@@ -47,57 +70,129 @@ func TestDefaults(t *testing.T) {
4770
})
4871

4972
t.Run("DefaultUpdateFunc", func(t *testing.T) {
50-
gotFP := reflect.ValueOf(DefaultUpdateFunc).Pointer()
51-
gotFuncName := runtime.FuncForPC(gotFP).Name()
52-
wantFP := reflect.ValueOf(EnvUpdateFunc).Pointer()
53-
wantFuncName := runtime.FuncForPC(wantFP).Name()
54-
assert.Equal(t, wantFuncName, gotFuncName)
73+
assertSameFunc(t, EnvUpdateFunc, DefaultUpdateFunc)
74+
})
75+
76+
t.Run("customized Default* variables", func(t *testing.T) {
77+
// Capture the default values before we change them.
78+
defaultDirMode := DefaultDirMode
79+
defaultFileMode := DefaultFileMode
80+
defaultSuffix := DefaultSuffix
81+
defaultDirname := DefaultDirname
82+
defaultUpdateFunc := DefaultUpdateFunc
83+
84+
// Restore the default values after the test.
85+
t.Cleanup(func() {
86+
DefaultDirMode = defaultDirMode
87+
DefaultFileMode = defaultFileMode
88+
DefaultSuffix = defaultSuffix
89+
DefaultDirname = defaultDirname
90+
DefaultUpdateFunc = defaultUpdateFunc
91+
})
92+
93+
// Set all the default values to new values.
94+
DefaultDirMode = os.FileMode(0o700)
95+
DefaultFileMode = os.FileMode(0o600)
96+
DefaultSuffix = ".gold"
97+
DefaultDirname = "goldenfiles"
98+
99+
updateFunc := func() bool { return true }
100+
DefaultUpdateFunc = updateFunc
101+
102+
// Create a new Golden instance with the new values.
103+
got := New()
104+
105+
assert.Equal(t, DefaultDirMode, got.DirMode)
106+
assert.Equal(t, DefaultFileMode, got.FileMode)
107+
assert.Equal(t, DefaultSuffix, got.Suffix)
108+
assert.Equal(t, DefaultDirname, got.Dirname)
109+
assertSameFunc(t, updateFunc, got.UpdateFunc)
55110
})
56111
}
57112

58-
// TestNew is a horribly hack to test that the New() function uses the
59-
// package-level Default* variables.
60113
func TestNew(t *testing.T) {
61-
// Capture the default values before we change them.
62-
defaultDirMode := DefaultDirMode
63-
defaultFileMode := DefaultFileMode
64-
defaultSuffix := DefaultSuffix
65-
defaultDirname := DefaultDirname
66-
defaultUpdateFunc := DefaultUpdateFunc
67-
68-
// Restore the default values after the test.
69-
t.Cleanup(func() {
70-
DefaultDirMode = defaultDirMode
71-
DefaultFileMode = defaultFileMode
72-
DefaultSuffix = defaultSuffix
73-
DefaultDirname = defaultDirname
74-
DefaultUpdateFunc = defaultUpdateFunc
114+
t.Run("defaults (no options)", func(t *testing.T) {
115+
g := New()
116+
assert.Equal(t, DefaultDirMode, g.DirMode)
117+
assert.Equal(t, DefaultFileMode, g.FileMode)
118+
assert.Equal(t, DefaultSuffix, g.Suffix)
119+
assert.Equal(t, DefaultDirname, g.Dirname)
120+
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
75121
})
76122

77-
// Set all the default values to new values.
78-
DefaultDirMode = os.FileMode(0o700)
79-
DefaultFileMode = os.FileMode(0o600)
80-
DefaultSuffix = ".gold"
81-
DefaultDirname = "goldenfiles"
123+
// Test each option individually
124+
t.Run("WithDirMode", func(t *testing.T) {
125+
customMode := os.FileMode(0o700)
126+
g := New(WithDirMode(customMode))
127+
assert.Equal(t, customMode, g.DirMode)
128+
assert.Equal(t, DefaultFileMode, g.FileMode)
129+
assert.Equal(t, DefaultSuffix, g.Suffix)
130+
assert.Equal(t, DefaultDirname, g.Dirname)
131+
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
132+
})
82133

83-
updateFunc := func() bool { return true }
84-
DefaultUpdateFunc = updateFunc
134+
t.Run("WithFileMode", func(t *testing.T) {
135+
customMode := os.FileMode(0o600)
136+
g := New(WithFileMode(customMode))
137+
assert.Equal(t, DefaultDirMode, g.DirMode)
138+
assert.Equal(t, customMode, g.FileMode)
139+
assert.Equal(t, DefaultSuffix, g.Suffix)
140+
assert.Equal(t, DefaultDirname, g.Dirname)
141+
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
142+
})
85143

86-
// Create a new Golden instance with the new values.
87-
got := New()
144+
t.Run("WithSuffix", func(t *testing.T) {
145+
customSuffix := ".my-suffix"
146+
g := New(WithSuffix(customSuffix))
147+
assert.Equal(t, DefaultDirMode, g.DirMode)
148+
assert.Equal(t, DefaultFileMode, g.FileMode)
149+
assert.Equal(t, customSuffix, g.Suffix)
150+
assert.Equal(t, DefaultDirname, g.Dirname)
151+
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
152+
})
88153

89-
assert.Equal(t, DefaultDirMode, got.DirMode)
90-
assert.Equal(t, DefaultFileMode, got.FileMode)
91-
assert.Equal(t, DefaultSuffix, got.Suffix)
92-
assert.Equal(t, DefaultDirname, got.Dirname)
154+
t.Run("WithDirname", func(t *testing.T) {
155+
customDirname := "fixtures/generated"
156+
g := New(WithDirname(customDirname))
157+
assert.Equal(t, DefaultDirMode, g.DirMode)
158+
assert.Equal(t, DefaultFileMode, g.FileMode)
159+
assert.Equal(t, DefaultSuffix, g.Suffix)
160+
assert.Equal(t, customDirname, g.Dirname)
161+
assertSameFunc(t, EnvUpdateFunc, g.UpdateFunc)
162+
})
93163

94-
// Verify the UpdateFunc value is set to the new value.
95-
gotFP := reflect.ValueOf(got.UpdateFunc).Pointer()
96-
gotFuncName := runtime.FuncForPC(gotFP).Name()
97-
wantFP := reflect.ValueOf(updateFunc).Pointer()
98-
wantFuncName := runtime.FuncForPC(wantFP).Name()
164+
t.Run("WithUpdateFunc", func(t *testing.T) {
165+
customUpdateFunc := func() bool { return true }
166+
g := New(WithUpdateFunc(customUpdateFunc))
167+
assert.Equal(t, DefaultDirMode, g.DirMode)
168+
assert.Equal(t, DefaultFileMode, g.FileMode)
169+
assert.Equal(t, DefaultSuffix, g.Suffix)
170+
assert.Equal(t, DefaultDirname, g.Dirname)
171+
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
172+
})
99173

100-
assert.Equal(t, wantFuncName, gotFuncName)
174+
// Test multiple options at once
175+
t.Run("MultipleOptions", func(t *testing.T) {
176+
customDirMode := os.FileMode(0o700)
177+
customFileMode := os.FileMode(0o600)
178+
customSuffix := ".fixture"
179+
customDirname := "fixtures"
180+
customUpdateFunc := func() bool { return true }
181+
182+
g := New(
183+
WithDirMode(customDirMode),
184+
WithFileMode(customFileMode),
185+
WithSuffix(customSuffix),
186+
WithDirname(customDirname),
187+
WithUpdateFunc(customUpdateFunc),
188+
)
189+
190+
assert.Equal(t, customDirMode, g.DirMode)
191+
assert.Equal(t, customFileMode, g.FileMode)
192+
assert.Equal(t, customSuffix, g.Suffix)
193+
assert.Equal(t, customDirname, g.Dirname)
194+
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
195+
})
101196
}
102197

103198
func TestDo(t *testing.T) {

options.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package golden
2+
3+
import (
4+
"os"
5+
)
6+
7+
// Option is a function that modifies a Golden instance.
8+
type Option func(*Golden)
9+
10+
// WithDirMode sets the directory mode for a Golden instance.
11+
func WithDirMode(mode os.FileMode) Option {
12+
return func(g *Golden) {
13+
g.DirMode = mode
14+
}
15+
}
16+
17+
// WithFileMode sets the file mode for a Golden instance.
18+
func WithFileMode(mode os.FileMode) Option {
19+
return func(g *Golden) {
20+
g.FileMode = mode
21+
}
22+
}
23+
24+
// WithSuffix sets the file suffix for a Golden instance.
25+
func WithSuffix(suffix string) Option {
26+
return func(g *Golden) {
27+
g.Suffix = suffix
28+
}
29+
}
30+
31+
// WithDirname sets the directory name for a Golden instance.
32+
func WithDirname(dirname string) Option {
33+
return func(g *Golden) {
34+
g.Dirname = dirname
35+
}
36+
}
37+
38+
// WithUpdateFunc sets the update function for a Golden instance.
39+
func WithUpdateFunc(updateFunc UpdateFunc) Option {
40+
return func(g *Golden) {
41+
g.UpdateFunc = updateFunc
42+
}
43+
}

options_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package golden
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
func TestWithDirMode(t *testing.T) {
11+
customMode := os.FileMode(0o700)
12+
g := &Golden{}
13+
14+
opt := WithDirMode(customMode)
15+
opt(g)
16+
17+
assert.Equal(t, customMode, g.DirMode)
18+
}
19+
20+
func TestWithFileMode(t *testing.T) {
21+
customMode := os.FileMode(0o600)
22+
g := &Golden{}
23+
24+
opt := WithFileMode(customMode)
25+
opt(g)
26+
27+
assert.Equal(t, customMode, g.FileMode)
28+
}
29+
30+
func TestWithSuffix(t *testing.T) {
31+
customSuffix := ".custom"
32+
g := &Golden{}
33+
34+
opt := WithSuffix(customSuffix)
35+
opt(g)
36+
37+
assert.Equal(t, customSuffix, g.Suffix)
38+
}
39+
40+
func TestWithDirname(t *testing.T) {
41+
customDirname := "custom-testdata"
42+
g := &Golden{}
43+
44+
opt := WithDirname(customDirname)
45+
opt(g)
46+
47+
assert.Equal(t, customDirname, g.Dirname)
48+
}
49+
50+
func TestWithUpdateFunc(t *testing.T) {
51+
customUpdateFunc := func() bool { return true }
52+
g := &Golden{}
53+
54+
opt := WithUpdateFunc(customUpdateFunc)
55+
opt(g)
56+
57+
assertSameFunc(t, customUpdateFunc, g.UpdateFunc)
58+
}

0 commit comments

Comments
 (0)