0
# Expression Factory and Generation
1
2
Factory pattern for creating expressions and advanced expression generation from example text, enabling automated step definition creation and flexible expression management.
3
4
## Capabilities
5
6
### Expression Factory
7
8
Create expressions from strings or regular expressions with automatic type detection and proper configuration.
9
10
```typescript { .api }
11
/**
12
* Factory for creating expressions with automatic type detection
13
*/
14
class ExpressionFactory {
15
/**
16
* Create expression factory with parameter type registry
17
* @param parameterTypeRegistry - Registry containing parameter types
18
*/
19
constructor(parameterTypeRegistry: ParameterTypeRegistry);
20
21
/**
22
* Create expression from string or RegExp with automatic type detection
23
* @param expression - Cucumber expression string or regular expression
24
* @returns CucumberExpression for strings, RegularExpression for RegExp objects
25
*/
26
createExpression(expression: string | RegExp): Expression;
27
}
28
```
29
30
**Usage Examples:**
31
32
```typescript
33
import { ExpressionFactory, ParameterTypeRegistry, CucumberExpression, RegularExpression } from '@cucumber/cucumber-expressions';
34
35
const registry = new ParameterTypeRegistry();
36
const factory = new ExpressionFactory(registry);
37
38
// Create Cucumber expression from string
39
const cucumberExpr = factory.createExpression('I have {int} cucumbers');
40
console.log(cucumberExpr instanceof CucumberExpression); // true
41
console.log(cucumberExpr.source); // "I have {int} cucumbers"
42
43
// Create regular expression from RegExp
44
const regexExpr = factory.createExpression(/^I have (\d+) cucumbers$/);
45
console.log(regexExpr instanceof RegularExpression); // true
46
console.log(regexExpr.regexp.source); // "^I have (\\d+) cucumbers$"
47
48
// Auto-detection of regular expression patterns
49
const anchoredExpr = factory.createExpression('^I have (\\d+) items$');
50
console.log(anchoredExpr instanceof RegularExpression); // true
51
52
const slashExpr = factory.createExpression('/I have (\\d+) items/');
53
console.log(slashExpr instanceof RegularExpression); // true
54
55
// Use expressions for matching
56
const text = 'I have 42 cucumbers';
57
const cucumberArgs = cucumberExpr.match(text);
58
const regexArgs = regexExpr.match(text);
59
60
console.log(cucumberArgs?.[0].getValue()); // 42 (typed as number)
61
console.log(regexArgs?.[0].getValue()); // "42" (as string)
62
```
63
64
**Python Implementation:**
65
66
```python { .api }
67
# Python expression factory pattern (manual implementation)
68
from cucumber_expressions import CucumberExpression, RegularExpression, ParameterTypeRegistry
69
import re
70
71
class ExpressionFactory:
72
def __init__(self, parameter_type_registry: ParameterTypeRegistry):
73
self.parameter_type_registry = parameter_type_registry
74
75
def create_expression(self, expression_string: str):
76
"""Create expression with automatic type detection"""
77
if self._is_regular_expression(expression_string):
78
pattern = self._extract_regex_pattern(expression_string)
79
return RegularExpression(re.compile(pattern), self.parameter_type_registry)
80
else:
81
return CucumberExpression(expression_string, self.parameter_type_registry)
82
83
def _is_regular_expression(self, expression: str) -> bool:
84
return (expression.startswith('^') and expression.endswith('$')) or \
85
(expression.startswith('/') and expression.endswith('/'))
86
87
def _extract_regex_pattern(self, expression: str) -> str:
88
if expression.startswith('/') and expression.endswith('/'):
89
return expression[1:-1]
90
return expression
91
```
92
93
**Java Implementation:**
94
95
```java { .api }
96
/**
97
* Factory for creating expressions with automatic type detection
98
*/
99
public final class ExpressionFactory {
100
/**
101
* Create expression factory with parameter type registry
102
* @param parameterTypeRegistry Registry for parameter types
103
*/
104
public ExpressionFactory(ParameterTypeRegistry parameterTypeRegistry);
105
106
/**
107
* Create expression from string with automatic type detection
108
* @param expressionString Expression string (Cucumber or regex pattern)
109
* @return Expression instance
110
*/
111
public Expression createExpression(String expressionString);
112
113
/**
114
* Create expression from Pattern
115
* @param pattern Regular expression pattern
116
* @return RegularExpression instance
117
*/
118
public Expression createExpression(Pattern pattern);
119
}
120
```
121
122
**Usage Examples:**
123
124
```java
125
import io.cucumber.cucumberexpressions.*;
126
import java.util.regex.Pattern;
127
128
ParameterTypeRegistry registry = new ParameterTypeRegistry();
129
ExpressionFactory factory = new ExpressionFactory(registry);
130
131
// Cucumber expression
132
Expression cucumberExpr = factory.createExpression("I have {int} items");
133
System.out.println(cucumberExpr.getClass().getSimpleName()); // "CucumberExpression"
134
135
// Regular expression with anchors
136
Expression regexExpr = factory.createExpression("^I have (\\d+) items$");
137
System.out.println(regexExpr.getClass().getSimpleName()); // "RegularExpression"
138
139
// Regular expression from Pattern
140
Pattern pattern = Pattern.compile("^Count: (\\d+)$");
141
Expression patternExpr = factory.createExpression(pattern);
142
System.out.println(patternExpr.getClass().getSimpleName()); // "RegularExpression"
143
```
144
145
### Expression Generation
146
147
Generate Cucumber expressions from example text for automated step definition creation and IDE tooling.
148
149
```typescript { .api }
150
/**
151
* Generates Cucumber expressions from example text
152
*/
153
class CucumberExpressionGenerator {
154
/**
155
* Create generator with parameter types supplier
156
* @param parameterTypes Function returning iterable of available parameter types
157
*/
158
constructor(parameterTypes: () => Iterable<ParameterType<unknown>>);
159
160
/**
161
* Generate expressions from example text
162
* @param text Example text to analyze and generate expressions from
163
* @returns Array of generated expressions ranked by specificity
164
*/
165
generateExpressions(text: string): readonly GeneratedExpression[];
166
}
167
168
/**
169
* Generated expression result with metadata
170
*/
171
class GeneratedExpression {
172
/**
173
* Create generated expression
174
* @param expressionTemplate Template with parameter placeholders
175
* @param parameterTypes Parameter types used in expression
176
*/
177
constructor(
178
expressionTemplate: string,
179
parameterTypes: readonly ParameterType<unknown>[]
180
);
181
182
/** The generated Cucumber expression */
183
readonly source: string;
184
185
/** Parameter names for code generation */
186
readonly parameterNames: readonly string[];
187
188
/** Parameter information for IDE integration */
189
readonly parameterInfos: readonly ParameterInfo[];
190
191
/** Parameter types in declaration order */
192
readonly parameterTypes: readonly ParameterType<unknown>[];
193
}
194
195
/**
196
* Parameter metadata for code generation
197
*/
198
interface ParameterInfo {
199
/** Parameter type name for code generation */
200
type: string | null;
201
/** Suggested parameter name */
202
name: string;
203
/** Number of occurrences in expression */
204
count: number;
205
}
206
```
207
208
**Usage Examples:**
209
210
```typescript
211
import { CucumberExpressionGenerator, ParameterTypeRegistry } from '@cucumber/cucumber-expressions';
212
213
const registry = new ParameterTypeRegistry();
214
const generator = new CucumberExpressionGenerator(() => registry.parameterTypes);
215
216
// Generate from text with integers
217
const expressions1 = generator.generateExpressions('I have 42 cucumbers');
218
console.log(expressions1.length); // Multiple alternatives
219
console.log(expressions1[0].source); // "I have {int} cucumbers"
220
console.log(expressions1[0].parameterNames); // ["int"]
221
console.log(expressions1[0].parameterInfos[0]); // { type: "int", name: "int", count: 1 }
222
223
// Generate from text with multiple parameters
224
const expressions2 = generator.generateExpressions('User john has 5 apples and 3.5 oranges');
225
expressions2.forEach((expr, index) => {
226
console.log(`Option ${index + 1}: ${expr.source}`);
227
console.log(`Parameters: ${expr.parameterTypes.map(pt => pt.name).join(', ')}`);
228
});
229
// Output might include:
230
// "User {word} has {int} {word} and {float} {word}"
231
// "User {word} has {int} apples and {float} oranges"
232
233
// Generate from text with quoted strings
234
const expressions3 = generator.generateExpressions('I select "Premium Plan" option');
235
console.log(expressions3[0].source); // "I select {string} option"
236
console.log(expressions3[0].parameterNames); // ["string"]
237
238
// Generate from complex scenarios
239
const expressions4 = generator.generateExpressions('On 2023-12-25 at 14:30, send email to user@example.com');
240
expressions4.forEach(expr => {
241
console.log(expr.source);
242
// Might generate expressions for dates, times, emails if custom types are registered
243
});
244
```
245
246
**Python Implementation:**
247
248
```python { .api }
249
class CucumberExpressionGenerator:
250
def __init__(self, parameter_type_registry: ParameterTypeRegistry):
251
"""Create generator with parameter type registry"""
252
self.parameter_type_registry = parameter_type_registry
253
254
def generate_expressions(self, text: str) -> List[GeneratedExpression]:
255
"""Generate expressions from example text"""
256
...
257
258
@staticmethod
259
def escape(string: str) -> str:
260
"""Escape special regex characters in string"""
261
...
262
263
class GeneratedExpression:
264
def __init__(self, expression_template: str, parameter_types: List[ParameterType]):
265
self.source = expression_template
266
self.parameter_types = parameter_types
267
self.parameter_names = [pt.name for pt in parameter_types]
268
```
269
270
**Usage Examples:**
271
272
```python
273
from cucumber_expressions import CucumberExpressionGenerator, ParameterTypeRegistry
274
275
registry = ParameterTypeRegistry()
276
generator = CucumberExpressionGenerator(registry)
277
278
# Generate from example text
279
expressions = generator.generate_expressions("I have 42 items")
280
for expr in expressions:
281
print(f"Expression: {expr.source}")
282
print(f"Parameters: {[pt.name for pt in expr.parameter_types]}")
283
284
# Generate with custom parameter types
285
from cucumber_expressions import ParameterType
286
287
color_type = ParameterType(
288
name="color",
289
regexp=r"red|green|blue",
290
type=str,
291
transformer=str.upper
292
)
293
registry.define_parameter_type(color_type)
294
295
expressions = generator.generate_expressions("I have a red car")
296
print(expressions[0].source) # Might be "I have a {color} car" if color is detected
297
```
298
299
**Java Implementation:**
300
301
```java { .api }
302
/**
303
* Generates Cucumber expressions from example text
304
*/
305
public final class CucumberExpressionGenerator {
306
/**
307
* Create generator with parameter type registry
308
* @param parameterTypeRegistry Registry containing available parameter types
309
*/
310
public CucumberExpressionGenerator(ParameterTypeRegistry parameterTypeRegistry);
311
312
/**
313
* Generate expressions from example text
314
* @param text Example text to analyze
315
* @return List of generated expressions
316
*/
317
public List<GeneratedExpression> generateExpressions(String text);
318
}
319
320
/**
321
* Generated expression with metadata
322
*/
323
public final class GeneratedExpression {
324
/**
325
* Create generated expression
326
* @param expressionTemplate Template string with parameters
327
* @param parameterTypes List of parameter types
328
*/
329
public GeneratedExpression(String expressionTemplate, List<ParameterType<?>> parameterTypes);
330
331
/** Get the generated expression source */
332
public String getSource();
333
334
/** Get parameter names for code generation */
335
public List<String> getParameterNames();
336
337
/** Get parameter information */
338
public List<ParameterInfo> getParameterInfos();
339
340
/** Get parameter types */
341
public List<ParameterType<?>> getParameterTypes();
342
}
343
344
/**
345
* Parameter information for code generation
346
*/
347
public final class ParameterInfo {
348
/** Parameter type name */
349
public String getType();
350
351
/** Suggested parameter name */
352
public String getName();
353
354
/** Parameter occurrence count */
355
public int getCount();
356
}
357
```
358
359
**Usage Examples:**
360
361
```java
362
import io.cucumber.cucumberexpressions.*;
363
import java.util.List;
364
365
ParameterTypeRegistry registry = new ParameterTypeRegistry();
366
CucumberExpressionGenerator generator = new CucumberExpressionGenerator(registry);
367
368
// Generate from simple text
369
List<GeneratedExpression> expressions = generator.generateExpressions("I have 42 items");
370
GeneratedExpression first = expressions.get(0);
371
System.out.println("Expression: " + first.getSource()); // "I have {int} items"
372
System.out.println("Parameter count: " + first.getParameterTypes().size()); // 1
373
374
// Generate method signatures for step definitions
375
for (ParameterInfo info : first.getParameterInfos()) {
376
System.out.printf("Parameter: %s %s%n", info.getType(), info.getName());
377
}
378
379
// Generate from complex text
380
List<GeneratedExpression> complex = generator.generateExpressions(
381
"User alice with age 25 has balance 123.45"
382
);
383
for (GeneratedExpression expr : complex) {
384
System.out.println("Generated: " + expr.getSource());
385
// Might output: "User {word} with age {int} has balance {float}"
386
}
387
```
388
389
### Expression Ranking and Selection
390
391
Generated expressions are ranked by specificity and usefulness for step definition creation.
392
393
```typescript { .api }
394
// Expression ranking factors:
395
// 1. More specific parameter types rank higher
396
// 2. Fewer anonymous parameters rank higher
397
// 3. More captured parameters rank higher
398
// 4. Built-in types preferred over custom types
399
```
400
401
**Usage Examples:**
402
403
```typescript
404
const expressions = generator.generateExpressions('Order #12345 costs $29.99');
405
406
// Results might be ranked as:
407
// 1. "Order #{int} costs ${float}" (most specific)
408
// 2. "Order #{word} costs ${float}" (less specific number)
409
// 3. "Order {} costs ${float}" (anonymous number)
410
// 4. "Order {} costs {}" (least specific)
411
412
console.log('Best match:', expressions[0].source);
413
console.log('Alternatives:');
414
expressions.slice(1).forEach((expr, index) => {
415
console.log(` ${index + 2}. ${expr.source}`);
416
});
417
```
418
419
### IDE Integration Support
420
421
Generated expressions provide metadata for IDE integration and code generation.
422
423
```typescript { .api }
424
// Code generation helpers
425
interface GeneratedExpression {
426
/** Generate method signature for step definition */
427
generateMethodSignature(language: 'typescript' | 'java' | 'python'): string;
428
429
/** Generate step definition template */
430
generateStepDefinition(language: 'typescript' | 'java' | 'python'): string;
431
}
432
```
433
434
**Usage Examples:**
435
436
```typescript
437
const expressions = generator.generateExpressions('User john has 5 items');
438
const best = expressions[0];
439
440
// TypeScript step definition generation
441
console.log('TypeScript:');
442
console.log(`Given('${best.source}', (${best.parameterNames.map((name, i) =>
443
`${name}: ${getTypeName(best.parameterTypes[i])}`
444
).join(', ')}) => {`);
445
console.log(' // Implementation here');
446
console.log('});');
447
448
// Java step definition generation
449
console.log('\nJava:');
450
console.log(`@Given("${best.source}")`);
451
console.log(`public void userHasItems(${best.parameterTypes.map((type, i) =>
452
`${getJavaType(type)} ${best.parameterNames[i]}`
453
).join(', ')}) {`);
454
console.log(' // Implementation here');
455
console.log('}');
456
457
function getTypeName(paramType: ParameterType<unknown>): string {
458
switch (paramType.name) {
459
case 'int': return 'number';
460
case 'float': return 'number';
461
case 'string': return 'string';
462
case 'word': return 'string';
463
default: return 'unknown';
464
}
465
}
466
467
function getJavaType(paramType: ParameterType<unknown>): string {
468
switch (paramType.name) {
469
case 'int': return 'Integer';
470
case 'float': return 'Float';
471
case 'string': return 'String';
472
case 'word': return 'String';
473
default: return 'Object';
474
}
475
}
476
```
477
478
### Advanced Generation Features
479
480
Support for complex text patterns and custom parameter type recognition.
481
482
```typescript { .api }
483
// Advanced generation with custom types
484
const emailType = new ParameterType('email', /[^@]+@[^.]+\..+/, String);
485
const dateType = new ParameterType('date', /\d{4}-\d{2}-\d{2}/, String);
486
487
registry.defineParameterType(emailType);
488
registry.defineParameterType(dateType);
489
490
const expressions = generator.generateExpressions(
491
'Send reminder to john@example.com on 2023-12-25'
492
);
493
494
console.log(expressions[0].source);
495
// "Send reminder to {email} on {date}"
496
```
497
498
**Escaping and Special Characters:**
499
500
```typescript
501
// Handle special characters in generated expressions
502
const textWithSpecialChars = 'File path is C:\\Program Files\\App (x86)';
503
const expressions = generator.generateExpressions(textWithSpecialChars);
504
505
// Generator automatically escapes special regex characters
506
console.log(expressions[0].source);
507
// "File path is C:\\\\Program Files\\\\App \\(x86\\)"
508
```
509
510
**Multi-Language Consistency:**
511
512
All language implementations provide consistent expression generation with the same ranking algorithms and output formats, ensuring portable step definitions across language boundaries in multi-language testing projects.
513
514
The expression factory and generation system enables powerful automation and tooling capabilities while maintaining the simplicity and readability that makes Cucumber Expressions superior to regular expressions for BDD step definition matching.