Skip to content

Commit c2a36e1

Browse files
authored
Add steps to set vars once for a feature/features, add factory expressions (#9)
1 parent a90b99b commit c2a36e1

File tree

8 files changed

+546
-35
lines changed

8 files changed

+546
-35
lines changed

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,6 @@ issues:
6565
- linters:
6666
- errcheck # Error checking omitted for brevity.
6767
- gosec
68+
- cyclop
6869
path: "example_"
6970

README.md

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,12 @@ Feature: Variables
4444
# Assert current value of variable.
4545
Then variable $foo equals to "abcdef"
4646
47+
# Variable can be set with user-defined factory.
48+
When variable $userId is set to newUserID("$foo", addDuration(now(), "-10h"))
49+
Then variable $userId equals to 12321
50+
4751
# Variable can be set with user-defined generator.
4852
When variable $foo is set to gen:new-id
49-
5053
Then variable $foo equals to 1337
5154
5255
# Set values to multiple variables.
@@ -134,4 +137,23 @@ You can enable variables in your own step definitions with these contextualized
134137
* `ReplaceFile` is same as `Replace`, but reads byte slice from a file,
135138
* `Assert` compares two byte slices, collects unknown vars, checks known vars,
136139
* `AssertFile` is same as `Assert`, but reads expected byte slice from a file,
137-
* `AssertJSONPaths` checks JSON byte slice against a `godog.Table` with expected values at JSON Paths.
140+
* `AssertJSONPaths` checks JSON byte slice against a `godog.Table` with expected values at JSON Paths.
141+
142+
### Setting variable once for multiple scenarios and/or features
143+
144+
In some cases you may want to set a variable only once in the feature or globally (in all features).
145+
146+
This is handy if you want to reuse same resources across whole feature or suite.
147+
148+
You can use factory function to produce a singleton with a named variable, see [`ExampleSteps_AddFactory`](./example_test.go).
149+
150+
```gherkin
151+
Given variables are set to values once in this feature
152+
| $user1 | newUserID("John Doe", addDuration(now(), "-10h")) |
153+
| $user2 | newUserID("Jane Doe", addDuration(now(), "-20h")) |
154+
155+
And variables are set to values once globally
156+
| $gv1 | gen:globalSeq |
157+
| $gv2 | gen:globalSeq |
158+
```
159+

_testdata/Feature1.feature

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Feature: feature 1
2+
3+
Scenario Outline: setting vars
4+
Given variables are set to values once in this feature
5+
| $fv1 | gen:featureSeq |
6+
| $fv2 | gen:featureSeq |
7+
8+
And variables are set to values once globally
9+
| $gv1 | gen:globalSeq |
10+
| $gv2 | gen:globalSeq |
11+
12+
And variables are set to values
13+
| $lv1 | <lv1> |
14+
| $lv2 | <lv2> |
15+
16+
Then variables are equal to values
17+
| $gv1 | 1 |
18+
| $gv2 | 2 |
19+
| $lv1 | <lv1> |
20+
| $lv2 | <lv2> |
21+
22+
Examples:
23+
| lv1 | lv2 |
24+
| 3 | "a" |
25+
| 4 | "b" |
26+
| 5 | "c" |
27+
| 6 | "d" |
28+
| 7 | "e" |

_testdata/Feature2.feature

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
Feature: feature 2
2+
3+
Scenario Outline: setting vars
4+
Given variables are set to values once in this feature
5+
| $fv1 | gen:featureSeq |
6+
| $fv2 | gen:featureSeq |
7+
8+
And variables are set to values once globally
9+
| $gv1 | gen:globalSeq |
10+
| $gv2 | gen:globalSeq |
11+
12+
And variables are set to values
13+
| $lv1 | <lv1> |
14+
| $lv2 | <lv2> |
15+
16+
Then variables are equal to values
17+
| $gv1 | 1 |
18+
| $gv2 | 2 |
19+
| $lv1 | <lv1> |
20+
| $lv2 | <lv2> |
21+
22+
Examples:
23+
| lv1 | lv2 |
24+
| 3 | "a" |
25+
| 4 | "b" |
26+
| 5 | "c" |
27+
| 6 | "d" |
28+
| 7 | "e" |

_testdata/Vars.feature

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@ Feature: Variables
1010
# Assert current value of variable.
1111
Then variable $foo equals to "abcdef"
1212

13+
# Variable can be set with user-defined factory.
14+
When variable $userId is set to newUserID("$foo", addDuration(now(), "-10h"))
15+
Then variable $userId equals to 12321
16+
1317
# Variable can be set with user-defined generator.
1418
When variable $foo is set to gen:new-id
15-
1619
Then variable $foo equals to 1337
1720

1821
# Set values to multiple variables.

example_test.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package vars_test
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"io"
8+
"time"
69

10+
"github.com/cucumber/godog"
711
"github.com/godogx/vars"
812
)
913

@@ -57,3 +61,111 @@ func ExampleReplace() {
5761
// Output:
5862
// {"foo":321,"bar":123,"prefixed_foo":"ooo::321"}
5963
}
64+
65+
func ExampleSteps_AddFactory() {
66+
vs := &vars.Steps{}
67+
68+
vs.AddFactory("now", func(ctx context.Context, args ...interface{}) (context.Context, interface{}, error) {
69+
// "Now" is mocked with a constant value to reproducibility.
70+
return ctx, time.Date(2023, 5, 22, 19, 38, 0, 0, time.UTC), nil
71+
})
72+
73+
vs.AddFactory("addDuration", func(ctx context.Context, args ...interface{}) (context.Context, interface{}, error) {
74+
if len(args) != 2 {
75+
return ctx, nil, errors.New("addDuration expects 2 arguments: base time, duration")
76+
}
77+
var (
78+
base time.Time
79+
dur time.Duration
80+
)
81+
82+
switch v := args[0].(type) {
83+
case time.Time:
84+
base = v
85+
case string:
86+
t, err := time.Parse(time.RFC3339Nano, v)
87+
if err != nil {
88+
return ctx, nil, fmt.Errorf("parsing base time: %w", err)
89+
}
90+
base = t
91+
default:
92+
return ctx, nil, fmt.Errorf("unexpected type %T for base time, string or time.Time expected", v)
93+
}
94+
95+
switch v := args[1].(type) {
96+
case time.Duration:
97+
dur = v
98+
case string:
99+
d, err := time.ParseDuration(v)
100+
if err != nil {
101+
return ctx, nil, fmt.Errorf("parsing duration: %w", err)
102+
}
103+
dur = d
104+
default:
105+
return ctx, nil, fmt.Errorf("unexpected type %T for duration, string or time.Duration expected", v)
106+
}
107+
108+
return ctx, base.Add(dur), nil
109+
})
110+
111+
vs.AddFactory("newUserID", func(ctx context.Context, args ...interface{}) (context.Context, interface{}, error) {
112+
if len(args) != 2 {
113+
return ctx, nil, errors.New("newUserID expects 2 arguments: name, registeredAt")
114+
}
115+
var (
116+
name string
117+
registeredAt time.Time
118+
)
119+
120+
switch v := args[0].(type) {
121+
case string:
122+
name = v
123+
default:
124+
return ctx, nil, fmt.Errorf("unexpected type %T for name, string expected", v)
125+
}
126+
127+
switch v := args[1].(type) {
128+
case time.Time:
129+
registeredAt = v
130+
case string:
131+
t, err := time.Parse(time.RFC3339Nano, v)
132+
if err != nil {
133+
return ctx, nil, fmt.Errorf("parsing registeredAt: %w", err)
134+
}
135+
registeredAt = t
136+
default:
137+
return ctx, nil, fmt.Errorf("unexpected type %T for registeredAt, string or time.Time expected", v)
138+
}
139+
140+
fmt.Println("creating user", name, registeredAt)
141+
142+
// Return relevant value, for example user id.
143+
return ctx, 123, nil
144+
})
145+
146+
s := godog.TestSuite{}
147+
148+
s.ScenarioInitializer = func(sc *godog.ScenarioContext) {
149+
vs.Register(sc)
150+
}
151+
152+
s.Options = &godog.Options{
153+
Format: "pretty",
154+
Output: io.Discard,
155+
FeatureContents: []godog.Feature{
156+
{
157+
Name: "example",
158+
Contents: []byte(`
159+
Feature: example
160+
Scenario: using var factory
161+
Given variable $myUserID is set to newUserID("John Doe", addDuration(now(), "-10h"))
162+
`),
163+
},
164+
},
165+
}
166+
167+
s.Run()
168+
169+
// Output:
170+
// creating user John Doe 2023-05-22 09:38:00 +0000 UTC
171+
}

0 commit comments

Comments
 (0)