or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-handling.mdhooks.mdindex.mdparameter-transformation.mdruntime-context.mdstep-definitions.md

parameter-transformation.mddocs/

0

# Parameter Transformation

1

2

Custom parameter types for converting step parameters from strings to domain objects, with extensive configuration options. Cucumber-Java provides a sophisticated transformation system including custom parameter types, default transformers, and data table transformations.

3

4

## Capabilities

5

6

### Custom Parameter Types

7

8

Register custom parameter types to convert strings from step definitions into domain objects using regular expressions.

9

10

```java { .api }

11

/**

12

* Register custom parameter types for Cucumber expressions

13

* @param value Regular expression pattern for matching

14

* @param name Parameter type name (default: method name)

15

* @param preferForRegexMatch Preferential type for regex matching (default: false)

16

* @param useForSnippets Use in snippet generation (default: false)

17

* @param useRegexpMatchAsStrongTypeHint Provide strong type hint (default: false)

18

*/

19

@ParameterType(value = "regex",

20

name = "typeName",

21

preferForRegexMatch = false,

22

useForSnippets = false,

23

useRegexpMatchAsStrongTypeHint = false)

24

public CustomType parameterTransformer(String input) {

25

return new CustomType(input);

26

}

27

28

/**

29

* Multi-capture group parameter type

30

*/

31

@ParameterType("(\\d+)-(\\d+)-(\\d+)")

32

public LocalDate date(String year, String month, String day) {

33

return LocalDate.of(Integer.parseInt(year),

34

Integer.parseInt(month),

35

Integer.parseInt(day));

36

}

37

```

38

39

**Usage Examples:**

40

41

```java

42

import io.cucumber.java.ParameterType;

43

import java.time.LocalDate;

44

import java.time.format.DateTimeFormatter;

45

46

public class ParameterTypes {

47

48

// Simple parameter type

49

@ParameterType("\\d{4}-\\d{2}-\\d{2}")

50

public LocalDate date(String dateString) {

51

return LocalDate.parse(dateString);

52

}

53

54

// Custom name parameter type

55

@ParameterType(value = "\\d+", name = "number")

56

public BigInteger bigNumber(String numberString) {

57

return new BigInteger(numberString);

58

}

59

60

// Multi-capture group parameter type

61

@ParameterType("(\\d{1,2})/(\\d{1,2})/(\\d{4})")

62

public LocalDate usDate(String month, String day, String year) {

63

return LocalDate.of(Integer.parseInt(year),

64

Integer.parseInt(month),

65

Integer.parseInt(day));

66

}

67

68

// Enum parameter type

69

@ParameterType("red|green|blue")

70

public Color color(String colorName) {

71

return Color.valueOf(colorName.toUpperCase());

72

}

73

74

// Complex object parameter type

75

@ParameterType("([A-Za-z]+)\\s+([A-Za-z]+)\\s+<([^>]+)>")

76

public Person person(String firstName, String lastName, String email) {

77

return new Person(firstName, lastName, email);

78

}

79

80

// Parameter type for snippets

81

@ParameterType(value = "\\d+\\.\\d{2}", useForSnippets = true)

82

public Money money(String amount) {

83

return new Money(new BigDecimal(amount));

84

}

85

86

// Preferential parameter type

87

@ParameterType(value = "\\d+", preferForRegexMatch = true)

88

public CustomNumber customNumber(String number) {

89

return new CustomNumber(Integer.parseInt(number));

90

}

91

}

92

93

// Usage in step definitions

94

@Given("the date is {date}")

95

public void the_date_is(LocalDate date) {

96

// date is automatically converted from string

97

}

98

99

@When("I transfer {money} to {person}")

100

public void transfer_money(Money amount, Person recipient) {

101

// Both parameters automatically converted

102

}

103

```

104

105

### Default Parameter Transformer

106

107

Register default parameter transformer for handling all unmatched parameter conversions.

108

109

```java { .api }

110

/**

111

* Register default parameter transformer

112

* Method signatures supported:

113

* - String, Type -> Object (transform from string)

114

* - Object, Type -> Object (transform from any object)

115

*/

116

@DefaultParameterTransformer

117

public Object defaultTransformer(String fromValue, Type toValueType) {

118

// Default transformation logic

119

return transformedValue;

120

}

121

122

/**

123

* Alternative signature for object transformation

124

*/

125

@DefaultParameterTransformer

126

public Object defaultTransformer(Object fromValue, Type toValueType) {

127

// Transform from any object type

128

return transformedValue;

129

}

130

```

131

132

**Usage Examples:**

133

134

```java

135

import io.cucumber.java.DefaultParameterTransformer;

136

import com.fasterxml.jackson.databind.ObjectMapper;

137

import java.lang.reflect.Type;

138

139

public class DefaultTransformers {

140

141

private final ObjectMapper objectMapper = new ObjectMapper();

142

143

// JSON-based default transformer using Jackson

144

@DefaultParameterTransformer

145

public Object defaultTransformer(String fromValue, Type toValueType) {

146

try {

147

// Try JSON transformation first

148

return objectMapper.readValue(fromValue,

149

objectMapper.constructType(toValueType));

150

} catch (Exception e) {

151

// Fallback to string-based transformation

152

if (toValueType.equals(Integer.class)) {

153

return Integer.valueOf(fromValue);

154

}

155

if (toValueType.equals(Boolean.class)) {

156

return Boolean.valueOf(fromValue);

157

}

158

if (toValueType.equals(LocalDate.class)) {

159

return LocalDate.parse(fromValue);

160

}

161

return fromValue; // Return as string if no transformation

162

}

163

}

164

165

// Enum-aware default transformer

166

@DefaultParameterTransformer

167

public Object enumTransformer(String fromValue, Type toValueType) {

168

if (toValueType instanceof Class && ((Class<?>) toValueType).isEnum()) {

169

Class<? extends Enum> enumClass = (Class<? extends Enum>) toValueType;

170

return Enum.valueOf(enumClass, fromValue.toUpperCase());

171

}

172

return fromValue;

173

}

174

}

175

176

// Usage - these will use default transformer

177

@Given("the status is {Status}") // Uses enum transformer

178

public void the_status_is(Status status) { }

179

180

@When("the config is {Config}") // Uses JSON transformer

181

public void the_config_is(Config config) { }

182

```

183

184

### Data Table Type Transformers

185

186

Register transformers for converting data table entries into custom objects.

187

188

```java { .api }

189

/**

190

* Register data table type transformer

191

* @param replaceWithEmptyString Values to replace with empty string (default: {})

192

*/

193

@DataTableType(replaceWithEmptyString = {"[blank]", "[empty]"})

194

public CustomObject tableEntryTransformer(Map<String, String> entry) {

195

return new CustomObject(entry);

196

}

197

198

/**

199

* Data table type for list transformation

200

*/

201

@DataTableType

202

public CustomObject listTransformer(List<String> row) {

203

return new CustomObject(row.get(0), row.get(1));

204

}

205

206

/**

207

* Data table type for single cell transformation

208

*/

209

@DataTableType

210

public CustomObject cellTransformer(String cell) {

211

return new CustomObject(cell);

212

}

213

```

214

215

**Usage Examples:**

216

217

```java

218

import io.cucumber.java.DataTableType;

219

import java.util.Map;

220

import java.util.List;

221

222

public class DataTableTypes {

223

224

// Transform table rows to Person objects

225

@DataTableType

226

public Person personEntry(Map<String, String> entry) {

227

return new Person(

228

entry.get("name"),

229

entry.get("email"),

230

Integer.parseInt(entry.get("age"))

231

);

232

}

233

234

// Transform with empty string replacement

235

@DataTableType(replaceWithEmptyString = {"", "[empty]", "null"})

236

public Product productEntry(Map<String, String> entry) {

237

return new Product(

238

entry.get("name"),

239

entry.get("description").isEmpty() ? null : entry.get("description"),

240

new BigDecimal(entry.get("price"))

241

);

242

}

243

244

// Transform list rows to objects

245

@DataTableType

246

public Coordinate coordinateRow(List<String> row) {

247

return new Coordinate(

248

Double.parseDouble(row.get(0)), // x

249

Double.parseDouble(row.get(1)) // y

250

);

251

}

252

253

// Single cell transformation

254

@DataTableType

255

public Currency currencyCell(String cell) {

256

return Currency.getInstance(cell);

257

}

258

}

259

260

// Usage in step definitions

261

@Given("the following users exist:")

262

public void users_exist(List<Person> users) {

263

// Each table row automatically converted to Person

264

}

265

266

@When("I create products:")

267

public void create_products(List<Product> products) {

268

// Each table row automatically converted to Product

269

}

270

```

271

272

### Default Data Table Transformers

273

274

Register default transformers for data table entries and cells.

275

276

```java { .api }

277

/**

278

* Register default data table entry transformer

279

* @param headersToProperties Convert headers to properties format (default: true)

280

* @param replaceWithEmptyString Values to replace with empty string (default: {})

281

*/

282

@DefaultDataTableEntryTransformer(headersToProperties = true,

283

replaceWithEmptyString = {})

284

public Object defaultEntryTransformer(Map<String, String> entry, Type type) {

285

return transformedEntry;

286

}

287

288

/**

289

* Register default data table cell transformer

290

* @param replaceWithEmptyString Values to replace with empty string (default: {})

291

*/

292

@DefaultDataTableCellTransformer(replaceWithEmptyString = {})

293

public Object defaultCellTransformer(String cell, Type type) {

294

return transformedCell;

295

}

296

```

297

298

**Usage Examples:**

299

300

```java

301

import io.cucumber.java.DefaultDataTableEntryTransformer;

302

import io.cucumber.java.DefaultDataTableCellTransformer;

303

import com.fasterxml.jackson.databind.ObjectMapper;

304

import java.lang.reflect.Type;

305

306

public class DefaultDataTableTransformers {

307

308

private final ObjectMapper objectMapper = new ObjectMapper();

309

310

// JSON-based default entry transformer

311

@DefaultDataTableEntryTransformer

312

public Object defaultEntryTransformer(Map<String, String> entry, Type type) {

313

try {

314

// Convert map to JSON then to target type

315

String json = objectMapper.writeValueAsString(entry);

316

return objectMapper.readValue(json,

317

objectMapper.constructType(type));

318

} catch (Exception e) {

319

throw new RuntimeException("Failed to transform entry: " + entry, e);

320

}

321

}

322

323

// Property-style header conversion

324

@DefaultDataTableEntryTransformer(headersToProperties = true)

325

public Object propertyEntryTransformer(Map<String, String> entry, Type type) {

326

// Headers like "First Name" become "firstName" properties

327

Map<String, String> propertyMap = entry.entrySet().stream()

328

.collect(Collectors.toMap(

329

e -> toCamelCase(e.getKey()),

330

Map.Entry::getValue

331

));

332

333

return createObjectFromProperties(propertyMap, type);

334

}

335

336

// Default cell transformer with multiple replacements

337

@DefaultDataTableCellTransformer(replaceWithEmptyString = {

338

"[blank]", "[empty]", "null", "N/A", ""

339

})

340

public Object defaultCellTransformer(String cell, Type type) {

341

// Handle common type conversions

342

if (type.equals(Integer.class) && !cell.isEmpty()) {

343

return Integer.valueOf(cell);

344

}

345

if (type.equals(Boolean.class)) {

346

return Boolean.valueOf(cell);

347

}

348

if (type.equals(LocalDate.class) && !cell.isEmpty()) {

349

return LocalDate.parse(cell);

350

}

351

return cell;

352

}

353

354

private String toCamelCase(String header) {

355

return Arrays.stream(header.split("\\s+"))

356

.map(word -> word.toLowerCase())

357

.collect(Collectors.joining())

358

.replaceFirst("^.", header.substring(0, 1).toLowerCase());

359

}

360

}

361

```

362

363

### Doc String Type Transformers

364

365

Register transformers for converting doc strings into custom objects.

366

367

```java { .api }

368

/**

369

* Register doc string type transformer

370

* @param contentType Content type for matching (default: "" - uses method name)

371

*/

372

@DocStringType(contentType = "json")

373

public CustomObject docStringTransformer(String docString) {

374

return parseDocString(docString);

375

}

376

377

/**

378

* Doc string type using method name as content type

379

*/

380

@DocStringType

381

public XmlDocument xml(String docString) {

382

return XmlDocument.parse(docString);

383

}

384

```

385

386

**Usage Examples:**

387

388

```java

389

import io.cucumber.java.DocStringType;

390

import com.fasterxml.jackson.databind.JsonNode;

391

import com.fasterxml.jackson.databind.ObjectMapper;

392

393

public class DocStringTypes {

394

395

private final ObjectMapper objectMapper = new ObjectMapper();

396

397

// JSON doc string transformer

398

@DocStringType

399

public JsonNode json(String docString) {

400

try {

401

return objectMapper.readTree(docString);

402

} catch (Exception e) {

403

throw new RuntimeException("Invalid JSON: " + docString, e);

404

}

405

}

406

407

// XML doc string transformer

408

@DocStringType

409

public Document xml(String docString) {

410

try {

411

DocumentBuilder builder = DocumentBuilderFactory

412

.newInstance()

413

.newDocumentBuilder();

414

return builder.parse(new InputSource(new StringReader(docString)));

415

} catch (Exception e) {

416

throw new RuntimeException("Invalid XML: " + docString, e);

417

}

418

}

419

420

// Custom content type

421

@DocStringType(contentType = "yaml")

422

public Map<String, Object> yamlContent(String docString) {

423

return yamlParser.parse(docString);

424

}

425

426

// Configuration object from properties

427

@DocStringType(contentType = "properties")

428

public Properties propertiesContent(String docString) {

429

Properties props = new Properties();

430

try {

431

props.load(new StringReader(docString));

432

return props;

433

} catch (Exception e) {

434

throw new RuntimeException("Invalid properties: " + docString, e);

435

}

436

}

437

}

438

439

// Usage in step definitions

440

@Given("the following JSON configuration:")

441

public void json_configuration(JsonNode config) {

442

// DocString automatically parsed as JSON

443

}

444

445

@When("I send the XML request:")

446

public void send_xml_request(Document xmlDoc) {

447

// DocString automatically parsed as XML

448

}

449

450

@Then("the YAML response should be:")

451

public void yaml_response(Map<String, Object> yaml) {

452

// DocString automatically parsed as YAML

453

}

454

```

455

456

### Data Table Transposition

457

458

Use the `@Transpose` annotation to transpose data tables before transformation.

459

460

```java { .api }

461

/**

462

* Transpose annotation for data table parameters

463

* @param value Whether to transpose the table (default: true)

464

*/

465

public void stepWithTransposedTable(@Transpose DataTable table) { }

466

467

/**

468

* Transpose with explicit value

469

*/

470

public void stepWithTransposedTable(@Transpose(true) List<Map<String, String>> data) { }

471

472

/**

473

* Conditional transposition

474

*/

475

public void stepWithConditionalTranspose(@Transpose(false) DataTable table) { }

476

```

477

478

**Usage Examples:**

479

480

```java

481

import io.cucumber.java.Transpose;

482

import io.cucumber.datatable.DataTable;

483

484

public class TransposeExamples {

485

486

// Transpose data table for vertical layout

487

@Given("the user has the following profile:")

488

public void user_profile(@Transpose DataTable profileData) {

489

// Original table: | name | John |

490

// | email | john@...|

491

// | age | 30 |

492

//

493

// After transpose: | name | email | age |

494

// | John | john@... | 30 |

495

}

496

497

// Transpose list of maps

498

@Given("the settings are configured:")

499

public void settings_configured(@Transpose List<Map<String, String>> settings) {

500

// Transposed before converting to list of maps

501

for (Map<String, String> setting : settings) {

502

String key = setting.get("key");

503

String value = setting.get("value");

504

}

505

}

506

507

// Conditional transpose

508

@Given("the matrix data:")

509

public void matrix_data(@Transpose(false) DataTable matrix) {

510

// Table is NOT transposed

511

}

512

513

// Transpose with custom objects

514

@Given("the product attributes:")

515

public void product_attributes(@Transpose List<Attribute> attributes) {

516

// Table transposed then converted to Attribute objects

517

}

518

}

519

```