or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

dotnet.mdexpression-factory.mdgo.mdindex.mdjava.mdjavascript-typescript.mdparameter-types.mdpython.mdruby.md

dotnet.mddocs/

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.