0
# Go Implementation
1
2
Go implementation with idiomatic Go patterns, reflection-based type handling, comprehensive error management, and full integration with Go testing frameworks.
3
4
## Package Information
5
6
- **Module**: `github.com/cucumber/cucumber-expressions/go/v18`
7
- **Package**: `cucumberexpressions`
8
- **Language**: Go 1.18+
9
- **Installation**: `go get github.com/cucumber/cucumber-expressions/go/v18`
10
11
## Core Imports
12
13
```go
14
import (
15
"github.com/cucumber/cucumber-expressions/go/v18"
16
"reflect"
17
"regexp"
18
)
19
```
20
21
## Capabilities
22
23
### Expression Creation and Matching
24
25
Create and match Cucumber expressions with Go-style error handling and type reflection.
26
27
```go { .api }
28
// CucumberExpression represents a parsed Cucumber expression
29
type CucumberExpression struct {
30
source string
31
parameterTypes []*ParameterType
32
treeRegexp *TreeRegexp
33
parameterTypeRegistry *ParameterTypeRegistry
34
}
35
36
// NewCucumberExpression creates a new Cucumber expression
37
// Returns Expression interface and error for Go error handling patterns
38
func NewCucumberExpression(expression string, parameterTypeRegistry *ParameterTypeRegistry) (Expression, error)
39
40
// Match text against this expression and extract arguments
41
// Uses reflection for type hints to support Go's static typing
42
func (c *CucumberExpression) Match(text string, typeHints ...reflect.Type) ([]*Argument, error)
43
44
// Regexp returns the compiled regular expression
45
func (c *CucumberExpression) Regexp() *regexp.Regexp
46
47
// Source returns the original expression string
48
func (c *CucumberExpression) Source() string
49
50
// Expression interface that all expression types implement
51
type Expression interface {
52
// Match text and return arguments with optional type hints
53
Match(text string, typeHints ...reflect.Type) ([]*Argument, error)
54
55
// Get compiled regular expression
56
Regexp() *regexp.Regexp
57
58
// Get source expression string
59
Source() string
60
}
61
```
62
63
**Usage Examples:**
64
65
```go
66
package main
67
68
import (
69
"fmt"
70
"log"
71
"reflect"
72
73
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
74
)
75
76
func main() {
77
// Create registry with built-in parameter types
78
registry := cucumberexpressions.NewParameterTypeRegistry()
79
80
// Simple integer parameter
81
expr1, err := cucumberexpressions.NewCucumberExpression("I have {int} cucumbers", registry)
82
if err != nil {
83
log.Fatal(err)
84
}
85
86
args1, err := expr1.Match("I have 42 cucumbers")
87
if err != nil {
88
log.Fatal(err)
89
}
90
if args1 != nil {
91
fmt.Printf("Count: %v (type: %T)\n", args1[0].GetValue(), args1[0].GetValue()) // 42 (int)
92
}
93
94
// Multiple parameters
95
expr2, err := cucumberexpressions.NewCucumberExpression("User {word} has {int} items", registry)
96
if err != nil {
97
log.Fatal(err)
98
}
99
100
args2, err := expr2.Match("User alice has 42 items")
101
if err != nil {
102
log.Fatal(err)
103
}
104
if args2 != nil {
105
username := args2[0].GetValue() // "alice" (string)
106
itemCount := args2[1].GetValue() // 42 (int)
107
fmt.Printf("User: %v, Items: %v\n", username, itemCount)
108
}
109
110
// Optional text
111
expr3, err := cucumberexpressions.NewCucumberExpression("I have {int} cucumber(s)", registry)
112
if err != nil {
113
log.Fatal(err)
114
}
115
116
args3a, _ := expr3.Match("I have 1 cucumber")
117
args3b, _ := expr3.Match("I have 5 cucumbers")
118
fmt.Printf("Singular match: %v\n", args3a != nil) // true
119
fmt.Printf("Plural match: %v\n", args3b != nil) // true
120
121
// Alternative text
122
expr4, err := cucumberexpressions.NewCucumberExpression("I put it in my belly/stomach", registry)
123
if err != nil {
124
log.Fatal(err)
125
}
126
127
args4a, _ := expr4.Match("I put it in my belly")
128
args4b, _ := expr4.Match("I put it in my stomach")
129
fmt.Printf("Belly match: %v\n", args4a != nil) // true
130
fmt.Printf("Stomach match: %v\n", args4b != nil) // true
131
132
// String parameters (removes quotes)
133
expr5, err := cucumberexpressions.NewCucumberExpression("I say {string}", registry)
134
if err != nil {
135
log.Fatal(err)
136
}
137
138
args5, err := expr5.Match(`I say "hello world"`)
139
if err != nil {
140
log.Fatal(err)
141
}
142
if args5 != nil {
143
fmt.Printf("Message: %v\n", args5[0].GetValue()) // "hello world" (without quotes)
144
}
145
146
// Type hints for anonymous parameters
147
expr6, err := cucumberexpressions.NewCucumberExpression("Value is {}", registry)
148
if err != nil {
149
log.Fatal(err)
150
}
151
152
args6, err := expr6.Match("Value is 42", reflect.TypeOf(0)) // Hint for int type
153
if err != nil {
154
log.Fatal(err)
155
}
156
if args6 != nil {
157
value := args6[0].GetValue()
158
fmt.Printf("Value: %v (type: %T)\n", value, value) // May be converted to int
159
}
160
}
161
```
162
163
### Parameter Type Definition
164
165
Define custom parameter types with Go-style struct initialization and function transformers.
166
167
```go { .api }
168
// ParameterType defines a parameter type with patterns and transformer
169
type ParameterType struct {
170
name string
171
regexps []*regexp.Regexp
172
type1 string
173
transform func(...*string) interface{}
174
useForSnippets bool
175
preferForRegexpMatch bool
176
useRegexpMatchAsStrongTypeHint bool
177
}
178
179
// NewParameterType creates a new parameter type with validation
180
func NewParameterType(
181
name string,
182
regexps []*regexp.Regexp,
183
type1 string,
184
transform func(...*string) interface{},
185
useForSnippets bool,
186
preferForRegexpMatch bool,
187
useRegexpMatchAsStrongTypeHint bool
188
) (*ParameterType, error)
189
190
// CheckParameterTypeName validates parameter type name
191
func CheckParameterTypeName(typeName string) error
192
193
// CompareParameterTypes compares two parameter types for sorting
194
func CompareParameterTypes(pt1, pt2 *ParameterType) int
195
196
// Accessor methods with Go naming conventions
197
func (p *ParameterType) Name() string
198
func (p *ParameterType) Regexps() []*regexp.Regexp
199
func (p *ParameterType) Type() string
200
func (p *ParameterType) UseForSnippets() bool
201
func (p *ParameterType) PreferForRegexpMatch() bool
202
func (p *ParameterType) UseRegexpMatchAsStrongTypeHint() bool
203
204
// Transform matched groups to target type
205
func (p *ParameterType) Transform(groupValues []*string) interface{}
206
```
207
208
**Usage Examples:**
209
210
```go
211
package main
212
213
import (
214
"fmt"
215
"log"
216
"regexp"
217
"strconv"
218
"strings"
219
"time"
220
221
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
222
)
223
224
// Custom types
225
type Color string
226
227
const (
228
Red Color = "RED"
229
Green Color = "GREEN"
230
Blue Color = "BLUE"
231
Yellow Color = "YELLOW"
232
)
233
234
type Point struct {
235
X, Y int
236
}
237
238
func (p Point) String() string {
239
return fmt.Sprintf("(%d, %d)", p.X, p.Y)
240
}
241
242
type Money struct {
243
Amount float64
244
Currency string
245
}
246
247
func (m Money) String() string {
248
return fmt.Sprintf("%.2f %s", m.Amount, m.Currency)
249
}
250
251
func main() {
252
registry := cucumberexpressions.NewParameterTypeRegistry()
253
254
// Color parameter type with enum-like behavior
255
colorRegexp, _ := regexp.Compile("red|green|blue|yellow")
256
colorType, err := cucumberexpressions.NewParameterType(
257
"color",
258
[]*regexp.Regexp{colorRegexp},
259
"Color",
260
func(args ...*string) interface{} {
261
if len(args) > 0 && args[0] != nil {
262
return Color(strings.ToUpper(*args[0]))
263
}
264
return nil
265
},
266
true, // useForSnippets
267
false, // preferForRegexpMatch
268
false, // useRegexpMatchAsStrongTypeHint
269
)
270
if err != nil {
271
log.Fatal(err)
272
}
273
274
// Point parameter type with multiple capture groups
275
pointRegexp, _ := regexp.Compile(`\((-?\d+),\s*(-?\d+)\)`)
276
pointType, err := cucumberexpressions.NewParameterType(
277
"point",
278
[]*regexp.Regexp{pointRegexp},
279
"Point",
280
func(args ...*string) interface{} {
281
if len(args) >= 2 && args[0] != nil && args[1] != nil {
282
x, _ := strconv.Atoi(*args[0])
283
y, _ := strconv.Atoi(*args[1])
284
return Point{X: x, Y: y}
285
}
286
return nil
287
},
288
true,
289
false,
290
false,
291
)
292
if err != nil {
293
log.Fatal(err)
294
}
295
296
// Date parameter type with time.Time
297
dateRegexp, _ := regexp.Compile(`(\d{4})-(\d{2})-(\d{2})`)
298
dateType, err := cucumberexpressions.NewParameterType(
299
"date",
300
[]*regexp.Regexp{dateRegexp},
301
"time.Time",
302
func(args ...*string) interface{} {
303
if len(args) >= 3 && args[0] != nil && args[1] != nil && args[2] != nil {
304
year, _ := strconv.Atoi(*args[0])
305
month, _ := strconv.Atoi(*args[1])
306
day, _ := strconv.Atoi(*args[2])
307
308
if month < 1 || month > 12 {
309
return fmt.Errorf("invalid month: %d", month)
310
}
311
if day < 1 || day > 31 {
312
return fmt.Errorf("invalid day: %d", day)
313
}
314
315
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
316
}
317
return nil
318
},
319
true,
320
false,
321
false,
322
)
323
if err != nil {
324
log.Fatal(err)
325
}
326
327
// Money parameter type with floating point
328
moneyRegexp, _ := regexp.Compile(`\$(\d+(?:\.\d{2})?) (USD|EUR|GBP)`)
329
moneyType, err := cucumberexpressions.NewParameterType(
330
"money",
331
[]*regexp.Regexp{moneyRegexp},
332
"Money",
333
func(args ...*string) interface{} {
334
if len(args) >= 2 && args[0] != nil && args[1] != nil {
335
amount, err := strconv.ParseFloat(*args[0], 64)
336
if err != nil {
337
return err
338
}
339
return Money{Amount: amount, Currency: strings.ToUpper(*args[1])}
340
}
341
return nil
342
},
343
true,
344
false,
345
false,
346
)
347
if err != nil {
348
log.Fatal(err)
349
}
350
351
// Register parameter types
352
err = registry.DefineParameterType(colorType)
353
if err != nil {
354
log.Fatal(err)
355
}
356
err = registry.DefineParameterType(pointType)
357
if err != nil {
358
log.Fatal(err)
359
}
360
err = registry.DefineParameterType(dateType)
361
if err != nil {
362
log.Fatal(err)
363
}
364
err = registry.DefineParameterType(moneyType)
365
if err != nil {
366
log.Fatal(err)
367
}
368
369
// Use in expressions
370
colorExpr, _ := cucumberexpressions.NewCucumberExpression("I have a {color} car", registry)
371
colorArgs, _ := colorExpr.Match("I have a red car")
372
if colorArgs != nil {
373
color := colorArgs[0].GetValue().(Color)
374
fmt.Printf("Color: %v\n", color) // RED
375
}
376
377
pointExpr, _ := cucumberexpressions.NewCucumberExpression("Move to {point}", registry)
378
pointArgs, _ := pointExpr.Match("Move to (10, -5)")
379
if pointArgs != nil {
380
point := pointArgs[0].GetValue().(Point)
381
fmt.Printf("Point: %v, X: %d, Y: %d\n", point, point.X, point.Y) // (10, -5), X: 10, Y: -5
382
}
383
384
dateExpr, _ := cucumberexpressions.NewCucumberExpression("Meeting on {date}", registry)
385
dateArgs, _ := dateExpr.Match("Meeting on 2023-12-25")
386
if dateArgs != nil {
387
date := dateArgs[0].GetValue().(time.Time)
388
fmt.Printf("Date: %v\n", date.Format("January 2, 2006")) // December 25, 2023
389
}
390
391
moneyExpr, _ := cucumberexpressions.NewCucumberExpression("Price is {money}", registry)
392
moneyArgs, _ := moneyExpr.Match("Price is $29.99 USD")
393
if moneyArgs != nil {
394
money := moneyArgs[0].GetValue().(Money)
395
fmt.Printf("Money: %v\n", money) // 29.99 USD
396
}
397
398
// Parameter type introspection
399
fmt.Printf("Color type name: %s\n", colorType.Name())
400
fmt.Printf("Color regexps: %v\n", colorType.Regexps())
401
fmt.Printf("Color use for snippets: %v\n", colorType.UseForSnippets())
402
}
403
```
404
405
### Parameter Type Registry
406
407
Go-style registry with map-based storage and error handling.
408
409
```go { .api }
410
// ParameterTypeRegistry manages parameter types with concurrent safety
411
type ParameterTypeRegistry struct {
412
parameterTypesByName map[string]*ParameterType
413
parameterTypesByRegexp map[string][]*ParameterType
414
defaultTransformer ParameterByTypeTransformer
415
}
416
417
// NewParameterTypeRegistry creates registry with built-in parameter types
418
func NewParameterTypeRegistry() *ParameterTypeRegistry
419
420
// LookupByTypeName finds parameter type by name
421
func (p *ParameterTypeRegistry) LookupByTypeName(name string) *ParameterType
422
423
// LookupByRegexp finds parameter type by regex pattern
424
func (p *ParameterTypeRegistry) LookupByRegexp(
425
parameterTypeRegexp string,
426
expressionRegexp *regexp.Regexp,
427
text string
428
) (*ParameterType, error)
429
430
// DefineParameterType registers a new parameter type
431
func (p *ParameterTypeRegistry) DefineParameterType(parameterType *ParameterType) error
432
433
// ParameterTypes returns all registered parameter types
434
func (p *ParameterTypeRegistry) ParameterTypes() []*ParameterType
435
436
// SetDefaultParameterTransformer sets transformer for anonymous parameters
437
func (p *ParameterTypeRegistry) SetDefaultParameterTransformer(transformer ParameterByTypeTransformer)
438
439
// ParameterByTypeTransformer interface for type-based parameter transformation
440
type ParameterByTypeTransformer interface {
441
Transform(fromValue string, toValueType reflect.Type) (interface{}, error)
442
}
443
```
444
445
**Usage Examples:**
446
447
```go
448
package main
449
450
import (
451
"fmt"
452
"log"
453
"reflect"
454
"regexp"
455
"strconv"
456
"strings"
457
458
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
459
)
460
461
// Custom transformer for anonymous parameters
462
type CustomTransformer struct{}
463
464
func (ct *CustomTransformer) Transform(fromValue string, toValueType reflect.Type) (interface{}, error) {
465
switch toValueType.Kind() {
466
case reflect.Int:
467
return strconv.Atoi(fromValue)
468
case reflect.Float64:
469
return strconv.ParseFloat(fromValue, 64)
470
case reflect.Bool:
471
return strconv.ParseBool(fromValue)
472
default:
473
return fromValue, nil
474
}
475
}
476
477
func main() {
478
registry := cucumberexpressions.NewParameterTypeRegistry()
479
480
// Check built-in types
481
intType := registry.LookupByTypeName("int")
482
if intType != nil {
483
fmt.Printf("Found int type: %s\n", intType.Name())
484
fmt.Printf("Int regexps: %v\n", intType.Regexps())
485
}
486
487
// List all parameter types
488
fmt.Println("All parameter types:")
489
for _, paramType := range registry.ParameterTypes() {
490
regexpStrs := make([]string, len(paramType.Regexps()))
491
for i, regex := range paramType.Regexps() {
492
regexpStrs[i] = regex.String()
493
}
494
fmt.Printf(" %s: %v\n", paramType.Name(), regexpStrs)
495
}
496
497
// Define custom parameter type
498
uuidRegexp, _ := regexp.Compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
499
uuidType, err := cucumberexpressions.NewParameterType(
500
"uuid",
501
[]*regexp.Regexp{uuidRegexp},
502
"string",
503
func(args ...*string) interface{} {
504
if len(args) > 0 && args[0] != nil {
505
return strings.ToUpper(*args[0])
506
}
507
return nil
508
},
509
true,
510
false,
511
false,
512
)
513
if err != nil {
514
log.Fatal(err)
515
}
516
517
err = registry.DefineParameterType(uuidType)
518
if err != nil {
519
log.Fatal(err)
520
}
521
522
// Verify registration
523
retrieved := registry.LookupByTypeName("uuid")
524
fmt.Printf("UUID type registered: %v\n", retrieved != nil)
525
fmt.Printf("Same instance: %v\n", retrieved == uuidType)
526
527
// Set custom default transformer
528
registry.SetDefaultParameterTransformer(&CustomTransformer{})
529
530
// Test with anonymous parameter and type hint
531
expr, _ := cucumberexpressions.NewCucumberExpression("Value is {}", registry)
532
533
// Test with int type hint
534
args, _ := expr.Match("Value is 42", reflect.TypeOf(0))
535
if args != nil {
536
value := args[0].GetValue()
537
fmt.Printf("Anonymous int: %v (type: %T)\n", value, value)
538
}
539
540
// Test with float type hint
541
args, _ = expr.Match("Value is 3.14", reflect.TypeOf(0.0))
542
if args != nil {
543
value := args[0].GetValue()
544
fmt.Printf("Anonymous float: %v (type: %T)\n", value, value)
545
}
546
}
547
```
548
549
### Expression Generation
550
551
Generate Cucumber expressions from example text with Go error handling patterns.
552
553
```go { .api }
554
// CucumberExpressionGenerator generates expressions from example text
555
type CucumberExpressionGenerator struct {
556
parameterTypeRegistry *ParameterTypeRegistry
557
}
558
559
// NewCucumberExpressionGenerator creates generator with parameter type registry
560
func NewCucumberExpressionGenerator(parameterTypeRegistry *ParameterTypeRegistry) *CucumberExpressionGenerator
561
562
// GenerateExpressions creates expressions from example text
563
func (c *CucumberExpressionGenerator) GenerateExpressions(text string) ([]*GeneratedExpression, error)
564
565
// GeneratedExpression contains generated expression with metadata
566
type GeneratedExpression struct {
567
source string
568
parameterTypes []*ParameterType
569
}
570
571
// Source returns the generated expression string
572
func (g *GeneratedExpression) Source() string
573
574
// ParameterTypes returns parameter types in declaration order
575
func (g *GeneratedExpression) ParameterTypes() []*ParameterType
576
577
// ParameterNames returns parameter names for code generation
578
func (g *GeneratedExpression) ParameterNames() []string
579
```
580
581
**Usage Examples:**
582
583
```go
584
package main
585
586
import (
587
"fmt"
588
"log"
589
590
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
591
)
592
593
func main() {
594
registry := cucumberexpressions.NewParameterTypeRegistry()
595
generator := cucumberexpressions.NewCucumberExpressionGenerator(registry)
596
597
// Generate from text with integers
598
expressions, err := generator.GenerateExpressions("I have 42 cucumbers")
599
if err != nil {
600
log.Fatal(err)
601
}
602
603
if len(expressions) > 0 {
604
best := expressions[0]
605
fmt.Printf("Generated: %s\n", best.Source()) // "I have {int} cucumbers"
606
fmt.Printf("Parameter names: %v\n", best.ParameterNames())
607
608
// Generate Go step definition
609
fmt.Println("Go step definition:")
610
paramTypes := make([]string, len(best.ParameterTypes()))
611
paramNames := best.ParameterNames()
612
for i, pt := range best.ParameterTypes() {
613
paramTypes[i] = goType(pt.Name())
614
}
615
616
fmt.Printf("func iHaveCucumbers(")
617
for i, name := range paramNames {
618
if i > 0 {
619
fmt.Print(", ")
620
}
621
fmt.Printf("%s %s", name, paramTypes[i])
622
}
623
fmt.Println(") error {")
624
fmt.Println(" // Implementation here")
625
fmt.Println(" return nil")
626
fmt.Println("}")
627
}
628
629
// Generate from complex text
630
expressions, err = generator.GenerateExpressions("User alice with age 25 has balance 123.45")
631
if err != nil {
632
log.Fatal(err)
633
}
634
635
fmt.Println("\nGenerated options:")
636
for i, expr := range expressions {
637
if i >= 3 { // Show first 3 options
638
break
639
}
640
641
typeNames := make([]string, len(expr.ParameterTypes()))
642
for j, pt := range expr.ParameterTypes() {
643
typeNames[j] = pt.Name()
644
}
645
646
fmt.Printf("%d. %s (%v)\n", i+1, expr.Source(), typeNames)
647
}
648
649
// Generate from text with strings
650
expressions, err = generator.GenerateExpressions(`I select "Premium Plan" option`)
651
if err != nil {
652
log.Fatal(err)
653
}
654
655
if len(expressions) > 0 {
656
fmt.Printf("String example: %s\n", expressions[0].Source()) // "I select {string} option"
657
}
658
}
659
660
func goType(paramTypeName string) string {
661
switch paramTypeName {
662
case "int":
663
return "int"
664
case "float":
665
return "float64"
666
case "string", "word":
667
return "string"
668
case "bigdecimal":
669
return "float64" // or big.Float for true precision
670
default:
671
return "interface{}"
672
}
673
}
674
```
675
676
### Argument Extraction
677
678
Extract matched arguments with Go-style value access and error handling.
679
680
```go { .api }
681
// Argument represents a matched argument with type information
682
type Argument struct {
683
group *Group
684
parameterType *ParameterType
685
}
686
687
// BuildArguments creates arguments from tree regexp match
688
func BuildArguments(treeRegexp *TreeRegexp, text string, parameterTypes []*ParameterType) []*Argument
689
690
// GetValue returns the transformed value
691
func (a *Argument) GetValue() interface{}
692
693
// GetParameterType returns the parameter type used for transformation
694
func (a *Argument) GetParameterType() *ParameterType
695
696
// Group returns the matched group with position information
697
func (a *Argument) Group() *Group
698
699
// Group represents a matched group with position and hierarchy
700
type Group struct {
701
Value *string
702
Start int
703
End int
704
Children []*Group
705
}
706
707
// GetValue returns the matched text value
708
func (g *Group) GetValue() *string
709
710
// GetStart returns start position in source text
711
func (g *Group) GetStart() int
712
713
// GetEnd returns end position in source text
714
func (g *Group) GetEnd() int
715
716
// GetChildren returns child groups
717
func (g *Group) GetChildren() []*Group
718
```
719
720
**Usage Examples:**
721
722
```go
723
package main
724
725
import (
726
"fmt"
727
"log"
728
729
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
730
)
731
732
func main() {
733
registry := cucumberexpressions.NewParameterTypeRegistry()
734
expr, err := cucumberexpressions.NewCucumberExpression("User {word} has {int} items", registry)
735
if err != nil {
736
log.Fatal(err)
737
}
738
739
args, err := expr.Match("User alice has 42 items")
740
if err != nil {
741
log.Fatal(err)
742
}
743
744
if args != nil {
745
// Type-safe value access
746
username := args[0].GetValue().(string) // "alice"
747
itemCount := args[1].GetValue().(int) // 42
748
749
fmt.Printf("User: %s, Items: %d\n", username, itemCount)
750
751
// Parameter type information
752
fmt.Printf("First param type: %s\n", args[0].GetParameterType().Name()) // "word"
753
fmt.Printf("Second param type: %s\n", args[1].GetParameterType().Name()) // "int"
754
755
// Position information
756
usernameGroup := args[0].Group()
757
if usernameGroup.GetValue() != nil {
758
fmt.Printf("Username '%s' at position %d-%d\n",
759
*usernameGroup.GetValue(),
760
usernameGroup.GetStart(),
761
usernameGroup.GetEnd())
762
}
763
764
itemGroup := args[1].Group()
765
if itemGroup.GetValue() != nil {
766
fmt.Printf("Item count '%s' at position %d-%d\n",
767
*itemGroup.GetValue(),
768
itemGroup.GetStart(),
769
itemGroup.GetEnd())
770
}
771
}
772
}
773
```
774
775
### Built-in Parameter Types
776
777
Go implementation provides built-in parameter types with appropriate Go type conversion.
778
779
```go { .api }
780
// Built-in parameter types in Go implementation:
781
782
{int} // Converts to int
783
{byte} // Converts to int8 (byte)
784
{short} // Converts to int16
785
{long} // Converts to int64
786
{float} // Converts to float32
787
{double} // Converts to float64
788
{biginteger} // Converts to big.Int
789
{bigdecimal} // Converts to big.Float
790
{word} // Returns as string (single word)
791
{string} // Returns as string (removes quotes)
792
{} // Anonymous - uses ParameterByTypeTransformer
793
```
794
795
**Usage Examples:**
796
797
```go
798
package main
799
800
import (
801
"fmt"
802
"log"
803
"math/big"
804
805
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
806
)
807
808
func main() {
809
registry := cucumberexpressions.NewParameterTypeRegistry()
810
811
// Test numeric built-in types
812
expr, err := cucumberexpressions.NewCucumberExpression(
813
"Numbers: {int} {float} {double} {word} {string}",
814
registry,
815
)
816
if err != nil {
817
log.Fatal(err)
818
}
819
820
args, err := expr.Match(`Numbers: 42 3.14 2.718281828 hello "world test"`)
821
if err != nil {
822
log.Fatal(err)
823
}
824
825
if args != nil {
826
intVal := args[0].GetValue().(int)
827
floatVal := args[1].GetValue().(float32)
828
doubleVal := args[2].GetValue().(float64)
829
wordVal := args[3].GetValue().(string)
830
stringVal := args[4].GetValue().(string)
831
832
fmt.Printf("int: %v (%T)\n", intVal, intVal) // 42 (int)
833
fmt.Printf("float: %v (%T)\n", floatVal, floatVal) // 3.14 (float32)
834
fmt.Printf("double: %v (%T)\n", doubleVal, doubleVal) // 2.718281828 (float64)
835
fmt.Printf("word: %v (%T)\n", wordVal, wordVal) // hello (string)
836
fmt.Printf("string: %v (%T)\n", stringVal, stringVal) // world test (string)
837
}
838
839
// Test big number types
840
bigExpr, err := cucumberexpressions.NewCucumberExpression(
841
"Big numbers: {biginteger} {bigdecimal}",
842
registry,
843
)
844
if err != nil {
845
log.Fatal(err)
846
}
847
848
bigArgs, err := bigExpr.Match("Big numbers: 12345678901234567890 123.456789012345")
849
if err != nil {
850
log.Fatal(err)
851
}
852
853
if bigArgs != nil {
854
bigIntVal := bigArgs[0].GetValue().(*big.Int)
855
bigDecVal := bigArgs[1].GetValue().(*big.Float)
856
857
fmt.Printf("biginteger: %v (%T)\n", bigIntVal, bigIntVal) // big.Int
858
fmt.Printf("bigdecimal: %v (%T)\n", bigDecVal, bigDecVal) // big.Float
859
}
860
}
861
```
862
863
### Integration with Go Testing Frameworks
864
865
Common patterns for integrating with Go testing frameworks and BDD tools.
866
867
```go
868
// Testing with standard Go testing package
869
package main
870
871
import (
872
"testing"
873
874
cucumberexpressions "github.com/cucumber/cucumber-expressions/go/v18"
875
)
876
877
func TestCucumberExpressions(t *testing.T) {
878
registry := cucumberexpressions.NewParameterTypeRegistry()
879
880
t.Run("ParameterExtraction", func(t *testing.T) {
881
expr, err := cucumberexpressions.NewCucumberExpression("User {word} has {int} items", registry)
882
if err != nil {
883
t.Fatal(err)
884
}
885
886
args, err := expr.Match("User alice has 42 items")
887
if err != nil {
888
t.Fatal(err)
889
}
890
891
if args == nil {
892
t.Fatal("Expected match but got nil")
893
}
894
895
if len(args) != 2 {
896
t.Fatalf("Expected 2 arguments, got %d", len(args))
897
}
898
899
username := args[0].GetValue().(string)
900
if username != "alice" {
901
t.Errorf("Expected username 'alice', got '%s'", username)
902
}
903
904
itemCount := args[1].GetValue().(int)
905
if itemCount != 42 {
906
t.Errorf("Expected item count 42, got %d", itemCount)
907
}
908
})
909
910
t.Run("CustomParameterType", func(t *testing.T) {
911
// Define custom parameter type for testing
912
colorRegexp, _ := regexp.Compile("red|green|blue")
913
colorType, err := cucumberexpressions.NewParameterType(
914
"color",
915
[]*regexp.Regexp{colorRegexp},
916
"string",
917
func(args ...*string) interface{} {
918
if len(args) > 0 && args[0] != nil {
919
return strings.ToUpper(*args[0])
920
}
921
return nil
922
},
923
true,
924
false,
925
false,
926
)
927
if err != nil {
928
t.Fatal(err)
929
}
930
931
err = registry.DefineParameterType(colorType)
932
if err != nil {
933
t.Fatal(err)
934
}
935
936
expr, err := cucumberexpressions.NewCucumberExpression("I have a {color} car", registry)
937
if err != nil {
938
t.Fatal(err)
939
}
940
941
args, err := expr.Match("I have a red car")
942
if err != nil {
943
t.Fatal(err)
944
}
945
946
if args == nil || len(args) != 1 {
947
t.Fatal("Expected 1 argument")
948
}
949
950
color := args[0].GetValue().(string)
951
if color != "RED" {
952
t.Errorf("Expected color 'RED', got '%s'", color)
953
}
954
})
955
}
956
957
// Step definition helper for BDD frameworks
958
type StepDefinition struct {
959
Pattern string
960
Expression cucumberexpressions.Expression
961
Handler interface{}
962
}
963
964
func NewStepDefinition(pattern string, registry *cucumberexpressions.ParameterTypeRegistry, handler interface{}) (*StepDefinition, error) {
965
expr, err := cucumberexpressions.NewCucumberExpression(pattern, registry)
966
if err != nil {
967
return nil, err
968
}
969
970
return &StepDefinition{
971
Pattern: pattern,
972
Expression: expr,
973
Handler: handler,
974
}, nil
975
}
976
977
func (sd *StepDefinition) Match(text string) ([]*cucumberexpressions.Argument, error) {
978
return sd.Expression.Match(text)
979
}
980
981
// Example usage in BDD framework integration
982
func ExampleBDDIntegration() {
983
registry := cucumberexpressions.NewParameterTypeRegistry()
984
985
// Define step definitions
986
stepDefs := []*StepDefinition{}
987
988
// Given step
989
givenStep, _ := NewStepDefinition(
990
"I have {int} cucumbers",
991
registry,
992
func(count int) error {
993
// Implementation
994
fmt.Printf("Setting cucumber count to %d\n", count)
995
return nil
996
},
997
)
998
stepDefs = append(stepDefs, givenStep)
999
1000
// When step
1001
whenStep, _ := NewStepDefinition(
1002
"I eat {int} cucumber(s)",
1003
registry,
1004
func(count int) error {
1005
// Implementation
1006
fmt.Printf("Eating %d cucumber(s)\n", count)
1007
return nil
1008
},
1009
)
1010
stepDefs = append(stepDefs, whenStep)
1011
1012
// Then step
1013
thenStep, _ := NewStepDefinition(
1014
"I should have {int} cucumber(s) left",
1015
registry,
1016
func(count int) error {
1017
// Implementation
1018
fmt.Printf("Checking %d cucumber(s) remaining\n", count)
1019
return nil
1020
},
1021
)
1022
stepDefs = append(stepDefs, thenStep)
1023
1024
// Execute scenario
1025
scenario := []string{
1026
"I have 5 cucumbers",
1027
"I eat 2 cucumbers",
1028
"I should have 3 cucumbers left",
1029
}
1030
1031
for _, step := range scenario {
1032
for _, stepDef := range stepDefs {
1033
args, err := stepDef.Match(step)
1034
if err != nil {
1035
fmt.Printf("Error matching step: %v\n", err)
1036
continue
1037
}
1038
if args != nil {
1039
fmt.Printf("Matched: %s\n", step)
1040
// Call handler with extracted arguments
1041
break
1042
}
1043
}
1044
}
1045
}
1046
```
1047
1048
The Go implementation provides idiomatic Go patterns with comprehensive error handling, reflection-based type hints, and efficient regex matching while maintaining full compatibility with the Cucumber Expressions specification and enabling seamless integration with Go testing frameworks.