0
# .NET Implementation
1
2
.NET Standard 2.0 implementation with C# language features, interface-based design, comprehensive exception handling, and full integration with .NET testing frameworks.
3
4
## Package Information
5
6
- **NuGet Package**: `Cucumber.CucumberExpressions`
7
- **Namespace**: `CucumberExpressions`
8
- **Target Framework**: .NET Standard 2.0
9
- **Language**: C# 7.0+
10
- **Installation**: `dotnet add package Cucumber.CucumberExpressions`
11
12
## Core Imports
13
14
```csharp
15
using CucumberExpressions;
16
using System;
17
using System.Collections.Generic;
18
using System.Text.RegularExpressions;
19
```
20
21
## Capabilities
22
23
### Expression Creation and Matching
24
25
Create and match Cucumber expressions with C# language features and .NET conventions.
26
27
```csharp { .api }
28
namespace CucumberExpressions
29
{
30
/// <summary>
31
/// Main class for parsing and matching Cucumber expressions
32
/// </summary>
33
public class CucumberExpression : IExpression
34
{
35
/// <summary>
36
/// Create a new Cucumber expression
37
/// </summary>
38
/// <param name="expression">The Cucumber expression string</param>
39
/// <param name="parameterTypeRegistry">Registry containing parameter types</param>
40
/// <exception cref="CucumberExpressionException">Thrown when expression is invalid</exception>
41
public CucumberExpression(string expression, IParameterTypeRegistry parameterTypeRegistry);
42
43
/// <summary>
44
/// Gets the original expression string
45
/// </summary>
46
public string Source { get; }
47
48
/// <summary>
49
/// Gets the compiled regular expression
50
/// </summary>
51
public Regex Regex { get; }
52
53
/// <summary>
54
/// Gets the parameter types used in this expression
55
/// </summary>
56
public IParameterType[] ParameterTypes { get; }
57
58
/// <summary>
59
/// Override for custom group naming in derived classes
60
/// </summary>
61
/// <param name="node">AST node</param>
62
/// <param name="parameterType">Parameter type</param>
63
/// <returns>Group name</returns>
64
protected virtual string GetGroupName(Node node, IParameterType parameterType);
65
66
/// <summary>
67
/// Handle string parameter type processing
68
/// </summary>
69
/// <param name="name">Parameter name</param>
70
/// <param name="parameterType">Parameter type</param>
71
/// <param name="regexps">Output regex patterns</param>
72
/// <param name="shouldWrapWithCaptureGroup">Whether to wrap with capture group</param>
73
/// <returns>True if handled</returns>
74
protected virtual bool HandleStringType(
75
string name,
76
IParameterType parameterType,
77
out string[] regexps,
78
out bool shouldWrapWithCaptureGroup);
79
80
/// <summary>
81
/// Get regex patterns for parameter type
82
/// </summary>
83
/// <param name="name">Parameter name</param>
84
/// <param name="parameterType">Parameter type</param>
85
/// <param name="shouldWrapWithCaptureGroup">Whether to wrap with capture group</param>
86
/// <returns>Regex patterns</returns>
87
protected virtual string[] GetParameterTypeRegexps(
88
string name,
89
IParameterType parameterType,
90
out bool shouldWrapWithCaptureGroup);
91
92
/// <summary>
93
/// Rewrite parameter node to regex
94
/// </summary>
95
/// <param name="node">Parameter node</param>
96
/// <returns>Regex string</returns>
97
protected virtual string RewriteParameter(Node node);
98
}
99
100
/// <summary>
101
/// Common interface for all expression types
102
/// </summary>
103
public interface IExpression
104
{
105
/// <summary>
106
/// Gets the source expression string
107
/// </summary>
108
string Source { get; }
109
110
/// <summary>
111
/// Gets the compiled regular expression
112
/// </summary>
113
Regex Regex { get; }
114
}
115
}
116
```
117
118
**Usage Examples:**
119
120
```csharp
121
using CucumberExpressions;
122
using System;
123
using System.Collections.Generic;
124
125
class Program
126
{
127
static void Main()
128
{
129
// Create parameter type registry (implementation needed)
130
var registry = new ParameterTypeRegistry(); // Custom implementation
131
132
try
133
{
134
// Simple integer parameter
135
var expr1 = new CucumberExpression("I have {int} cucumbers", registry);
136
Console.WriteLine($"Expression: {expr1.Source}");
137
Console.WriteLine($"Regex: {expr1.Regex}");
138
139
// Multiple parameters
140
var expr2 = new CucumberExpression("User {word} has {int} items", registry);
141
Console.WriteLine($"Parameter types: {expr2.ParameterTypes.Length}");
142
143
// Optional text
144
var expr3 = new CucumberExpression("I have {int} cucumber(s)", registry);
145
Console.WriteLine($"Optional expression: {expr3.Source}");
146
147
// Alternative text
148
var expr4 = new CucumberExpression("I put it in my belly/stomach", registry);
149
Console.WriteLine($"Alternative expression: {expr4.Source}");
150
151
// String parameters (removes quotes)
152
var expr5 = new CucumberExpression("I say {string}", registry);
153
Console.WriteLine($"String expression: {expr5.Source}");
154
155
// Expression introspection
156
foreach (var paramType in expr2.ParameterTypes)
157
{
158
Console.WriteLine($"Parameter: {paramType.Name} - Weight: {paramType.Weight}");
159
}
160
}
161
catch (CucumberExpressionException ex)
162
{
163
Console.WriteLine($"Expression error: {ex.Message}");
164
}
165
catch (UndefinedParameterTypeException ex)
166
{
167
Console.WriteLine($"Undefined parameter type: {ex.Message}");
168
}
169
}
170
}
171
```
172
173
### Expression Factory
174
175
Factory for creating expressions with automatic type detection and .NET patterns.
176
177
```csharp { .api }
178
namespace CucumberExpressions
179
{
180
/// <summary>
181
/// Factory for creating expressions with automatic type detection
182
/// </summary>
183
public class ExpressionFactory
184
{
185
/// <summary>
186
/// Create expression factory with parameter type registry
187
/// </summary>
188
/// <param name="parameterTypeRegistry">Registry for parameter types</param>
189
public ExpressionFactory(IParameterTypeRegistry parameterTypeRegistry);
190
191
/// <summary>
192
/// Create expression from string with automatic type detection
193
/// </summary>
194
/// <param name="expressionString">Expression string or regex pattern</param>
195
/// <returns>Expression instance</returns>
196
/// <exception cref="CucumberExpressionException">Thrown when expression is invalid</exception>
197
public IExpression CreateExpression(string expressionString);
198
199
/// <summary>
200
/// Determine if string represents a regular expression
201
/// </summary>
202
/// <param name="expressionString">Expression string to test</param>
203
/// <returns>True if regex pattern</returns>
204
protected virtual bool IsRegularExpression(string expressionString);
205
}
206
}
207
```
208
209
**Usage Examples:**
210
211
```csharp
212
using CucumberExpressions;
213
using System;
214
215
class ExpressionFactoryExample
216
{
217
static void Main()
218
{
219
var registry = new ParameterTypeRegistry(); // Custom implementation
220
var factory = new ExpressionFactory(registry);
221
222
try
223
{
224
// Create Cucumber expression
225
var cucumberExpr = factory.CreateExpression("I have {int} items");
226
Console.WriteLine($"Type: {cucumberExpr.GetType().Name}"); // CucumberExpression
227
Console.WriteLine($"Source: {cucumberExpr.Source}");
228
229
// Create regular expression (anchored)
230
var regexExpr = factory.CreateExpression("^I have (\\d+) items$");
231
Console.WriteLine($"Type: {regexExpr.GetType().Name}"); // RegularExpression
232
Console.WriteLine($"Source: {regexExpr.Source}");
233
234
// Create regular expression (with slashes)
235
var slashExpr = factory.CreateExpression("/I have (\\d+) items/");
236
Console.WriteLine($"Type: {slashExpr.GetType().Name}"); // RegularExpression
237
238
// Both implement IExpression
239
ProcessExpression(cucumberExpr);
240
ProcessExpression(regexExpr);
241
}
242
catch (CucumberExpressionException ex)
243
{
244
Console.WriteLine($"Error: {ex.Message}");
245
}
246
}
247
248
static void ProcessExpression(IExpression expression)
249
{
250
Console.WriteLine($"Processing: {expression.Source}");
251
Console.WriteLine($"Regex: {expression.Regex.ToString()}");
252
}
253
}
254
```
255
256
### Regular Expression Support
257
258
Support for traditional regular expressions alongside Cucumber expressions.
259
260
```csharp { .api }
261
namespace CucumberExpressions
262
{
263
/// <summary>
264
/// Regular expression implementation of IExpression interface
265
/// </summary>
266
public class RegularExpression : IExpression
267
{
268
/// <summary>
269
/// Create regular expression from System.Text.RegularExpressions.Regex
270
/// </summary>
271
/// <param name="regex">Regular expression pattern</param>
272
public RegularExpression(Regex regex);
273
274
/// <summary>
275
/// Gets the source pattern string
276
/// </summary>
277
public string Source { get; }
278
279
/// <summary>
280
/// Gets the regular expression
281
/// </summary>
282
public Regex Regex { get; }
283
}
284
}
285
```
286
287
**Usage Examples:**
288
289
```csharp
290
using CucumberExpressions;
291
using System;
292
using System.Text.RegularExpressions;
293
294
class RegularExpressionExample
295
{
296
static void Main()
297
{
298
// Create from Regex object
299
var regex = new Regex(@"^I have (\d+) items$", RegexOptions.Compiled);
300
var regexExpr = new RegularExpression(regex);
301
302
Console.WriteLine($"Source: {regexExpr.Source}");
303
Console.WriteLine($"Pattern: {regexExpr.Regex.ToString()}");
304
Console.WriteLine($"Options: {regexExpr.Regex.Options}");
305
306
// Test matching
307
var match = regexExpr.Regex.Match("I have 42 items");
308
if (match.Success)
309
{
310
Console.WriteLine($"Matched: {match.Value}");
311
Console.WriteLine($"Group 1: {match.Groups[1].Value}"); // "42"
312
}
313
314
// Interface usage
315
IExpression expression = regexExpr;
316
Console.WriteLine($"Interface source: {expression.Source}");
317
Console.WriteLine($"Interface regex: {expression.Regex}");
318
}
319
}
320
```
321
322
### Interfaces and Parameter Types
323
324
Comprehensive interface-based design with parameter type support.
325
326
```csharp { .api }
327
namespace CucumberExpressions
328
{
329
/// <summary>
330
/// Interface for parameter type definitions
331
/// </summary>
332
public interface IParameterType
333
{
334
/// <summary>
335
/// Gets the regular expression strings for matching
336
/// </summary>
337
string[] RegexStrings { get; }
338
339
/// <summary>
340
/// Gets the parameter type name
341
/// </summary>
342
string Name { get; }
343
344
/// <summary>
345
/// Gets the .NET type this parameter converts to
346
/// </summary>
347
Type ParameterType { get; }
348
349
/// <summary>
350
/// Gets the weight for parameter type ranking
351
/// </summary>
352
int Weight { get; }
353
354
/// <summary>
355
/// Gets whether this type should be used for snippet generation
356
/// </summary>
357
bool UseForSnippets { get; }
358
}
359
360
/// <summary>
361
/// Interface for parameter type registry
362
/// </summary>
363
public interface IParameterTypeRegistry
364
{
365
/// <summary>
366
/// Look up parameter type by name
367
/// </summary>
368
/// <param name="name">Parameter type name</param>
369
/// <returns>Parameter type or null if not found</returns>
370
IParameterType LookupByTypeName(string name);
371
}
372
}
373
```
374
375
**Usage Examples:**
376
377
```csharp
378
using CucumberExpressions;
379
using System;
380
using System.Linq;
381
382
// Custom parameter type implementation
383
public class ColorParameterType : IParameterType
384
{
385
public string[] RegexStrings => new[] { "red|green|blue|yellow" };
386
public string Name => "color";
387
public Type ParameterType => typeof(string);
388
public int Weight => 1;
389
public bool UseForSnippets => true;
390
391
public object Transform(string value)
392
{
393
return value.ToUpperInvariant();
394
}
395
}
396
397
// Custom registry implementation
398
public class ParameterTypeRegistry : IParameterTypeRegistry
399
{
400
private readonly Dictionary<string, IParameterType> _parameterTypes;
401
402
public ParameterTypeRegistry()
403
{
404
_parameterTypes = new Dictionary<string, IParameterType>
405
{
406
["int"] = new IntParameterType(),
407
["float"] = new FloatParameterType(),
408
["word"] = new WordParameterType(),
409
["string"] = new StringParameterType(),
410
["color"] = new ColorParameterType()
411
};
412
}
413
414
public IParameterType LookupByTypeName(string name)
415
{
416
return _parameterTypes.TryGetValue(name, out var parameterType) ? parameterType : null;
417
}
418
419
public void DefineParameterType(IParameterType parameterType)
420
{
421
_parameterTypes[parameterType.Name] = parameterType;
422
}
423
}
424
425
class ParameterTypeExample
426
{
427
static void Main()
428
{
429
var registry = new ParameterTypeRegistry();
430
431
// Look up built-in types
432
var intType = registry.LookupByTypeName("int");
433
Console.WriteLine($"Int type: {intType?.Name} -> {intType?.ParameterType}");
434
435
// Look up custom types
436
var colorType = registry.LookupByTypeName("color");
437
Console.WriteLine($"Color type: {colorType?.Name}");
438
Console.WriteLine($"Color patterns: {string.Join(", ", colorType?.RegexStrings ?? new string[0])}");
439
440
// Define additional custom type
441
registry.DefineParameterType(new DateParameterType());
442
443
var dateType = registry.LookupByTypeName("date");
444
Console.WriteLine($"Date type registered: {dateType != null}");
445
}
446
}
447
448
// Additional parameter type implementations
449
public class IntParameterType : IParameterType
450
{
451
public string[] RegexStrings => new[] { @"-?\d+" };
452
public string Name => "int";
453
public Type ParameterType => typeof(int);
454
public int Weight => 1;
455
public bool UseForSnippets => true;
456
}
457
458
public class DateParameterType : IParameterType
459
{
460
public string[] RegexStrings => new[] { @"(\d{4}-\d{2}-\d{2})" };
461
public string Name => "date";
462
public Type ParameterType => typeof(DateTime);
463
public int Weight => 1;
464
public bool UseForSnippets => true;
465
466
public object Transform(string value)
467
{
468
return DateTime.Parse(value);
469
}
470
}
471
```
472
473
### Exception Handling
474
475
Comprehensive exception hierarchy for error handling.
476
477
```csharp { .api }
478
namespace CucumberExpressions
479
{
480
/// <summary>
481
/// Base exception for all Cucumber expression errors
482
/// </summary>
483
public class CucumberExpressionException : Exception
484
{
485
/// <summary>
486
/// Create exception with message
487
/// </summary>
488
/// <param name="message">Error message</param>
489
public CucumberExpressionException(string message) : base(message) { }
490
491
/// <summary>
492
/// Create exception with message and inner exception
493
/// </summary>
494
/// <param name="message">Error message</param>
495
/// <param name="innerException">Inner exception</param>
496
public CucumberExpressionException(string message, Exception innerException) : base(message, innerException) { }
497
}
498
499
/// <summary>
500
/// Exception thrown when parameter type is not defined
501
/// </summary>
502
public class UndefinedParameterTypeException : CucumberExpressionException
503
{
504
/// <summary>
505
/// Create exception for undefined parameter type
506
/// </summary>
507
/// <param name="parameterTypeName">Name of undefined parameter type</param>
508
public UndefinedParameterTypeException(string parameterTypeName)
509
: base($"Undefined parameter type: {parameterTypeName}")
510
{
511
ParameterTypeName = parameterTypeName;
512
}
513
514
/// <summary>
515
/// Gets the name of the undefined parameter type
516
/// </summary>
517
public string ParameterTypeName { get; }
518
}
519
}
520
```
521
522
**Usage Examples:**
523
524
```csharp
525
using CucumberExpressions;
526
using System;
527
528
class ExceptionHandlingExample
529
{
530
static void Main()
531
{
532
var registry = new ParameterTypeRegistry();
533
534
try
535
{
536
// This will throw UndefinedParameterTypeException
537
var expr = new CucumberExpression("I have {undefined_type} items", registry);
538
}
539
catch (UndefinedParameterTypeException ex)
540
{
541
Console.WriteLine($"Undefined parameter type: {ex.ParameterTypeName}");
542
Console.WriteLine($"Full message: {ex.Message}");
543
}
544
catch (CucumberExpressionException ex)
545
{
546
Console.WriteLine($"Cucumber expression error: {ex.Message}");
547
}
548
catch (Exception ex)
549
{
550
Console.WriteLine($"Unexpected error: {ex.Message}");
551
}
552
553
// Safe parameter type lookup
554
var paramType = SafeLookupParameterType(registry, "nonexistent");
555
Console.WriteLine($"Safe lookup result: {paramType?.Name ?? "null"}");
556
}
557
558
static IParameterType SafeLookupParameterType(IParameterTypeRegistry registry, string name)
559
{
560
try
561
{
562
return registry.LookupByTypeName(name);
563
}
564
catch (Exception ex)
565
{
566
Console.WriteLine($"Error looking up parameter type '{name}': {ex.Message}");
567
return null;
568
}
569
}
570
}
571
```
572
573
### AST Support
574
575
Abstract Syntax Tree representation for advanced expression manipulation.
576
577
```csharp { .api }
578
namespace CucumberExpressions
579
{
580
/// <summary>
581
/// AST node representing parsed expression components
582
/// </summary>
583
public abstract class Node
584
{
585
/// <summary>
586
/// Gets the node type
587
/// </summary>
588
public abstract NodeType Type { get; }
589
590
/// <summary>
591
/// Gets child nodes
592
/// </summary>
593
public virtual Node[] Nodes { get; protected set; } = new Node[0];
594
595
/// <summary>
596
/// Gets associated token
597
/// </summary>
598
public virtual Token Token { get; protected set; }
599
600
/// <summary>
601
/// Gets text representation of this node
602
/// </summary>
603
/// <returns>Node text</returns>
604
public abstract string GetText();
605
}
606
607
/// <summary>
608
/// Token from lexical analysis
609
/// </summary>
610
public class Token
611
{
612
/// <summary>
613
/// Create token
614
/// </summary>
615
/// <param name="type">Token type</param>
616
/// <param name="text">Token text</param>
617
/// <param name="start">Start position</param>
618
/// <param name="end">End position</param>
619
public Token(TokenType type, string text, int start, int end)
620
{
621
Type = type;
622
Text = text;
623
Start = start;
624
End = end;
625
}
626
627
/// <summary>
628
/// Gets the token type
629
/// </summary>
630
public TokenType Type { get; }
631
632
/// <summary>
633
/// Gets the token text
634
/// </summary>
635
public string Text { get; }
636
637
/// <summary>
638
/// Gets the start position
639
/// </summary>
640
public int Start { get; }
641
642
/// <summary>
643
/// Gets the end position
644
/// </summary>
645
public int End { get; }
646
}
647
648
/// <summary>
649
/// AST node types
650
/// </summary>
651
public enum NodeType
652
{
653
Text,
654
Optional,
655
Alternation,
656
Alternative,
657
Parameter,
658
Expression
659
}
660
661
/// <summary>
662
/// Token types from lexer
663
/// </summary>
664
public enum TokenType
665
{
666
StartOfLine,
667
EndOfLine,
668
WhiteSpace,
669
BeginParameter,
670
EndParameter,
671
BeginOptional,
672
EndOptional,
673
BeginAlternation,
674
EndAlternation,
675
AlternationPipe,
676
Text
677
}
678
}
679
```
680
681
### Built-in Parameter Types
682
683
.NET implementation supports built-in parameter types with appropriate .NET type conversion.
684
685
```csharp { .api }
686
// Built-in parameter types in .NET implementation:
687
688
{int} // Converts to System.Int32
689
{byte} // Converts to System.Byte
690
{short} // Converts to System.Int16
691
{long} // Converts to System.Int64
692
{float} // Converts to System.Single
693
{double} // Converts to System.Double
694
{decimal} // Converts to System.Decimal
695
{word} // Returns as System.String (single word)
696
{string} // Returns as System.String (removes quotes)
697
{} // Anonymous - based on context or string
698
```
699
700
**Usage Examples:**
701
702
```csharp
703
using CucumberExpressions;
704
using System;
705
706
// Built-in parameter type implementations
707
public class IntParameterType : IParameterType
708
{
709
public string[] RegexStrings => new[] { @"-?\d+" };
710
public string Name => "int";
711
public Type ParameterType => typeof(int);
712
public int Weight => 1;
713
public bool UseForSnippets => true;
714
715
public object Transform(string value)
716
{
717
return int.Parse(value);
718
}
719
}
720
721
public class DecimalParameterType : IParameterType
722
{
723
public string[] RegexStrings => new[] { @"-?\d*\.?\d+" };
724
public string Name => "decimal";
725
public Type ParameterType => typeof(decimal);
726
public int Weight => 1;
727
public bool UseForSnippets => true;
728
729
public object Transform(string value)
730
{
731
return decimal.Parse(value);
732
}
733
}
734
735
public class StringParameterType : IParameterType
736
{
737
public string[] RegexStrings => new[] { "\"([^\"\\\\]*(\\\\.[^\"\\\\]*)*)\"|'([^'\\\\]*(\\\\.[^'\\\\]*)*)'" };
738
public string Name => "string";
739
public Type ParameterType => typeof(string);
740
public int Weight => 0;
741
public bool UseForSnippets => true;
742
743
public object Transform(string value)
744
{
745
// Remove quotes and handle escape sequences
746
if (value.StartsWith("\"") && value.EndsWith("\"") ||
747
value.StartsWith("'") && value.EndsWith("'"))
748
{
749
return value.Substring(1, value.Length - 2);
750
}
751
return value;
752
}
753
}
754
755
class BuiltInTypesExample
756
{
757
static void Main()
758
{
759
var registry = new ParameterTypeRegistry();
760
761
// Test numeric types
762
var intType = registry.LookupByTypeName("int");
763
var transformedInt = ((IntParameterType)intType).Transform("42");
764
Console.WriteLine($"Int: {transformedInt} ({transformedInt.GetType()})");
765
766
var decimalType = registry.LookupByTypeName("decimal");
767
var transformedDecimal = ((DecimalParameterType)decimalType).Transform("123.45");
768
Console.WriteLine($"Decimal: {transformedDecimal} ({transformedDecimal.GetType()})");
769
770
// Test string type
771
var stringType = registry.LookupByTypeName("string");
772
var transformedString = ((StringParameterType)stringType).Transform("\"hello world\"");
773
Console.WriteLine($"String: '{transformedString}' ({transformedString.GetType()})");
774
775
// Type information
776
foreach (var typeName in new[] { "int", "decimal", "string" })
777
{
778
var paramType = registry.LookupByTypeName(typeName);
779
Console.WriteLine($"{typeName}: {paramType.ParameterType} (Weight: {paramType.Weight})");
780
}
781
}
782
}
783
```
784
785
### Integration with .NET Testing Frameworks
786
787
Common patterns for integrating with NUnit, xUnit, MSTest, and SpecFlow.
788
789
```csharp
790
// NUnit integration example
791
using NUnit.Framework;
792
using CucumberExpressions;
793
using System;
794
795
[TestFixture]
796
public class CucumberExpressionTests
797
{
798
private IParameterTypeRegistry registry;
799
800
[SetUp]
801
public void Setup()
802
{
803
registry = new ParameterTypeRegistry();
804
}
805
806
[Test]
807
public void ShouldMatchIntegerParameter()
808
{
809
// Arrange
810
var expression = new CucumberExpression("I have {int} items", registry);
811
812
// Act & Assert
813
Assert.DoesNotThrow(() => expression.Regex.Match("I have 42 items"));
814
var match = expression.Regex.Match("I have 42 items");
815
Assert.IsTrue(match.Success);
816
Assert.AreEqual("42", match.Groups[1].Value);
817
}
818
819
[Test]
820
public void ShouldHandleOptionalText()
821
{
822
// Arrange
823
var expression = new CucumberExpression("I have {int} cucumber(s)", registry);
824
825
// Act & Assert
826
Assert.IsTrue(expression.Regex.IsMatch("I have 1 cucumber"));
827
Assert.IsTrue(expression.Regex.IsMatch("I have 5 cucumbers"));
828
Assert.IsFalse(expression.Regex.IsMatch("I have 5 tomatoes"));
829
}
830
831
[Test]
832
public void ShouldThrowForUndefinedParameterType()
833
{
834
// Act & Assert
835
var ex = Assert.Throws<UndefinedParameterTypeException>(() =>
836
new CucumberExpression("I have {undefined} items", registry));
837
838
Assert.AreEqual("undefined", ex.ParameterTypeName);
839
StringAssert.Contains("Undefined parameter type: undefined", ex.Message);
840
}
841
842
[TestCase("I have {int} items", "I have 42 items", ExpectedResult = true)]
843
[TestCase("I have {int} items", "I have many items", ExpectedResult = false)]
844
[TestCase("I have {word} items", "I have many items", ExpectedResult = true)]
845
public bool ShouldMatchVariousPatterns(string pattern, string text)
846
{
847
var expression = new CucumberExpression(pattern, registry);
848
return expression.Regex.IsMatch(text);
849
}
850
}
851
852
// xUnit integration example
853
using Xunit;
854
using CucumberExpressions;
855
856
public class ExpressionFactoryTests
857
{
858
private readonly IParameterTypeRegistry registry;
859
860
public ExpressionFactoryTests()
861
{
862
registry = new ParameterTypeRegistry();
863
}
864
865
[Fact]
866
public void ShouldCreateCucumberExpression()
867
{
868
var factory = new ExpressionFactory(registry);
869
var expression = factory.CreateExpression("I have {int} items");
870
871
Assert.IsType<CucumberExpression>(expression);
872
Assert.Equal("I have {int} items", expression.Source);
873
}
874
875
[Fact]
876
public void ShouldCreateRegularExpression()
877
{
878
var factory = new ExpressionFactory(registry);
879
var expression = factory.CreateExpression("^I have (\\d+) items$");
880
881
Assert.IsType<RegularExpression>(expression);
882
Assert.Equal("^I have (\\d+) items$", expression.Source);
883
}
884
885
[Theory]
886
[InlineData("I have {int} items", typeof(CucumberExpression))]
887
[InlineData("^I have (\\d+) items$", typeof(RegularExpression))]
888
[InlineData("/I have (\\d+) items/", typeof(RegularExpression))]
889
public void ShouldDetectExpressionType(string pattern, Type expectedType)
890
{
891
var factory = new ExpressionFactory(registry);
892
var expression = factory.CreateExpression(pattern);
893
894
Assert.IsType(expectedType, expression);
895
}
896
}
897
898
// SpecFlow integration example (step definition attribute usage)
899
using TechTalk.SpecFlow;
900
using CucumberExpressions;
901
902
[Binding]
903
public class StepDefinitions
904
{
905
private readonly ScenarioContext scenarioContext;
906
private IParameterTypeRegistry registry;
907
908
public StepDefinitions(ScenarioContext scenarioContext)
909
{
910
this.scenarioContext = scenarioContext;
911
this.registry = new ParameterTypeRegistry();
912
}
913
914
[Given(@"I have (\d+) cucumbers")]
915
public void GivenIHaveCucumbers(int count)
916
{
917
scenarioContext["CucumberCount"] = count;
918
}
919
920
[When(@"I eat (\d+) cucumbers?")]
921
public void WhenIEatCucumbers(int count)
922
{
923
var currentCount = (int)scenarioContext["CucumberCount"];
924
scenarioContext["CucumberCount"] = currentCount - count;
925
}
926
927
[Then(@"I should have (\d+) cucumbers? left")]
928
public void ThenIShouldHaveCucumbersLeft(int expectedCount)
929
{
930
var actualCount = (int)scenarioContext["CucumberCount"];
931
Assert.AreEqual(expectedCount, actualCount);
932
}
933
934
// Custom parameter type for SpecFlow
935
[StepArgumentTransformation(@"red|green|blue")]
936
public Color ConvertColor(string colorName)
937
{
938
return (Color)Enum.Parse(typeof(Color), colorName, ignoreCase: true);
939
}
940
941
[Given(@"I have a (red|green|blue) car")]
942
public void GivenIHaveColorCar(Color color)
943
{
944
scenarioContext["CarColor"] = color;
945
}
946
}
947
948
public enum Color { Red, Green, Blue }
949
950
// MSTest integration example
951
using Microsoft.VisualStudio.TestTools.UnitTesting;
952
using CucumberExpressions;
953
954
[TestClass]
955
public class ParameterTypeTests
956
{
957
private IParameterTypeRegistry registry;
958
959
[TestInitialize]
960
public void Initialize()
961
{
962
registry = new ParameterTypeRegistry();
963
}
964
965
[TestMethod]
966
public void ShouldLookupBuiltInParameterTypes()
967
{
968
var intType = registry.LookupByTypeName("int");
969
Assert.IsNotNull(intType);
970
Assert.AreEqual("int", intType.Name);
971
Assert.AreEqual(typeof(int), intType.ParameterType);
972
}
973
974
[TestMethod]
975
public void ShouldReturnNullForUnknownParameterType()
976
{
977
var unknownType = registry.LookupByTypeName("unknown");
978
Assert.IsNull(unknownType);
979
}
980
981
[DataTestMethod]
982
[DataRow("int", typeof(int))]
983
[DataRow("string", typeof(string))]
984
[DataRow("decimal", typeof(decimal))]
985
public void ShouldMapParameterTypeToNetType(string parameterTypeName, Type expectedType)
986
{
987
var parameterType = registry.LookupByTypeName(parameterTypeName);
988
Assert.IsNotNull(parameterType);
989
Assert.AreEqual(expectedType, parameterType.ParameterType);
990
}
991
}
992
```
993
994
The .NET implementation provides comprehensive C# language integration with interface-based design, robust exception handling, and seamless integration with popular .NET testing frameworks while maintaining full compatibility with the Cucumber Expressions specification and following .NET conventions and best practices.