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

data-handling.mddocs/

0

# Data Handling

1

2

Robust data table and doc string handling with custom transformations and built-in collection support. Cucumber-Java provides comprehensive support for handling complex test data through data tables and doc strings with automatic conversion to Java collections and custom objects.

3

4

## Capabilities

5

6

### Data Tables

7

8

Access Gherkin data tables as various Java collection types with automatic type conversion and custom transformations.

9

10

```java { .api }

11

/**

12

* Raw DataTable access for maximum flexibility

13

*/

14

public void stepWithDataTable(DataTable table) {

15

List<List<String>> rows = table.asLists();

16

List<Map<String, String>> maps = table.asMaps();

17

}

18

19

/**

20

* List of lists - each row as a list of strings

21

*/

22

public void stepWithListOfLists(List<List<String>> table) { }

23

24

/**

25

* List of maps - each row as a map with headers as keys

26

*/

27

public void stepWithListOfMaps(List<Map<String, String>> table) { }

28

29

/**

30

* Single map - first column as keys, second column as values

31

*/

32

public void stepWithMap(Map<String, String> table) { }

33

34

/**

35

* Map of lists - first column as keys, remaining columns as list values

36

*/

37

public void stepWithMapOfLists(Map<String, List<String>> table) { }

38

39

/**

40

* Map of maps - first column as keys, headers and values as nested maps

41

*/

42

public void stepWithMapOfMaps(Map<String, Map<String, String>> table) { }

43

```

44

45

**Usage Examples:**

46

47

```java

48

import io.cucumber.datatable.DataTable;

49

import io.cucumber.java.en.Given;

50

51

public class DataTableExamples {

52

53

// Raw DataTable for custom processing

54

@Given("the following data:")

55

public void raw_data_table(DataTable table) {

56

List<List<String>> rows = table.asLists();

57

58

// Process headers

59

List<String> headers = rows.get(0);

60

61

// Process data rows

62

for (int i = 1; i < rows.size(); i++) {

63

List<String> row = rows.get(i);

64

// Process each row

65

}

66

67

// Alternative: get as maps

68

List<Map<String, String>> maps = table.asMaps(String.class, String.class);

69

for (Map<String, String> rowMap : maps) {

70

String name = rowMap.get("name");

71

String email = rowMap.get("email");

72

}

73

}

74

75

// List of lists - simple table structure

76

@Given("the matrix:")

77

public void matrix_data(List<List<String>> matrix) {

78

for (List<String> row : matrix) {

79

for (String cell : row) {

80

// Process each cell

81

}

82

}

83

}

84

85

// List of maps - table with headers

86

@Given("the following users exist:")

87

public void users_exist(List<Map<String, String>> users) {

88

for (Map<String, String> user : users) {

89

String name = user.get("name");

90

String email = user.get("email");

91

int age = Integer.parseInt(user.get("age"));

92

93

// Create user object

94

createUser(name, email, age);

95

}

96

}

97

98

// Single map - key-value pairs

99

@Given("the configuration:")

100

public void configuration(Map<String, String> config) {

101

String environment = config.get("environment");

102

String database = config.get("database");

103

String timeout = config.get("timeout");

104

105

// Apply configuration

106

applyConfig(environment, database, timeout);

107

}

108

109

// Map of lists - grouped data

110

@Given("the grouped data:")

111

public void grouped_data(Map<String, List<String>> groups) {

112

List<String> adminUsers = groups.get("admins");

113

List<String> regularUsers = groups.get("users");

114

List<String> guests = groups.get("guests");

115

116

// Process each group

117

}

118

119

// Map of maps - hierarchical data

120

@Given("the user profiles:")

121

public void user_profiles(Map<String, Map<String, String>> profiles) {

122

Map<String, String> johnProfile = profiles.get("john");

123

String johnEmail = johnProfile.get("email");

124

String johnRole = johnProfile.get("role");

125

126

Map<String, String> janeProfile = profiles.get("jane");

127

String janeEmail = janeProfile.get("email");

128

String janeRole = janeProfile.get("role");

129

}

130

}

131

```

132

133

### Data Table Type Conversion

134

135

Automatic conversion of data tables to strongly-typed collections using built-in and custom type converters.

136

137

```java { .api }

138

/**

139

* Built-in numeric type conversions

140

*/

141

public void stepWithIntegers(List<List<Integer>> intTable) { }

142

public void stepWithDoubles(List<List<Double>> doubleTable) { }

143

public void stepWithFloats(List<List<Float>> floatTable) { }

144

public void stepWithLongs(List<List<Long>> longTable) { }

145

public void stepWithBigDecimals(List<List<BigDecimal>> decimalTable) { }

146

public void stepWithBigIntegers(List<List<BigInteger>> bigIntTable) { }

147

148

/**

149

* Custom object conversion using @DataTableType

150

*/

151

public void stepWithCustomObjects(List<CustomObject> objects) { }

152

```

153

154

**Usage Examples:**

155

156

```java

157

import java.math.BigDecimal;

158

import java.math.BigInteger;

159

160

public class TypedDataTableExamples {

161

162

// Integer data table

163

@Given("the following numbers:")

164

public void numbers(List<List<Integer>> numberTable) {

165

for (List<Integer> row : numberTable) {

166

for (Integer number : row) {

167

// Process integer values directly

168

processNumber(number);

169

}

170

}

171

}

172

173

// Double precision data

174

@Given("the coordinates:")

175

public void coordinates(List<Map<String, Double>> coords) {

176

for (Map<String, Double> coord : coords) {

177

Double x = coord.get("x");

178

Double y = coord.get("y");

179

Double z = coord.get("z");

180

181

// Work with double values directly

182

plotPoint(x, y, z);

183

}

184

}

185

186

// Financial data with BigDecimal

187

@Given("the following prices:")

188

public void prices(List<Map<String, BigDecimal>> priceData) {

189

for (Map<String, BigDecimal> item : priceData) {

190

String product = item.get("product").toString();

191

BigDecimal price = item.get("price");

192

BigDecimal tax = item.get("tax");

193

194

// Precise decimal calculations

195

BigDecimal total = price.add(tax);

196

}

197

}

198

199

// Custom object conversion (requires @DataTableType)

200

@Given("the following products:")

201

public void products(List<Product> products) {

202

for (Product product : products) {

203

// Work with strongly-typed Product objects

204

saveProduct(product);

205

}

206

}

207

208

// Mixed type map

209

@Given("the test data:")

210

public void test_data(Map<String, Object> data) {

211

// Note: requires @DefaultDataTableCellTransformer for Object conversion

212

String name = (String) data.get("name");

213

Integer count = (Integer) data.get("count");

214

Boolean active = (Boolean) data.get("active");

215

}

216

}

217

```

218

219

### Doc Strings

220

221

Access Gherkin doc strings as raw strings or convert to custom objects using doc string type transformers.

222

223

```java { .api }

224

/**

225

* Raw doc string access

226

*/

227

public void stepWithDocString(String docString) { }

228

229

/**

230

* DocString object with metadata

231

*/

232

public void stepWithDocStringObject(DocString docString) {

233

String content = docString.getContent();

234

String contentType = docString.getContentType();

235

}

236

237

/**

238

* Custom object conversion using @DocStringType

239

*/

240

public void stepWithCustomDocString(CustomObject parsedDocString) { }

241

```

242

243

**Usage Examples:**

244

245

```java

246

import io.cucumber.docstring.DocString;

247

import io.cucumber.java.en.Given;

248

249

public class DocStringExamples {

250

251

// Raw string doc string

252

@Given("the following JSON:")

253

public void json_data(String jsonString) {

254

// Parse JSON manually

255

ObjectMapper mapper = new ObjectMapper();

256

try {

257

JsonNode json = mapper.readTree(jsonString);

258

// Process JSON

259

} catch (Exception e) {

260

throw new RuntimeException("Invalid JSON", e);

261

}

262

}

263

264

// DocString object with metadata

265

@Given("the following document:")

266

public void document_data(DocString docString) {

267

String content = docString.getContent();

268

String contentType = docString.getContentType(); // From """ content_type

269

270

// Process based on content type

271

if ("json".equals(contentType)) {

272

processJsonContent(content);

273

} else if ("xml".equals(contentType)) {

274

processXmlContent(content);

275

} else {

276

processPlainText(content);

277

}

278

}

279

280

// Multi-line text processing

281

@Given("the following email template:")

282

public void email_template(String template) {

283

// Process multi-line email template

284

String[] lines = template.split("\n");

285

String subject = extractSubject(lines);

286

String body = extractBody(lines);

287

288

sendEmail(subject, body);

289

}

290

291

// Large text data

292

@Given("the following log entries:")

293

public void log_entries(String logData) {

294

String[] entries = logData.split("\n");

295

for (String entry : entries) {

296

if (!entry.trim().isEmpty()) {

297

parseLogEntry(entry);

298

}

299

}

300

}

301

302

// Custom parsed doc string (requires @DocStringType)

303

@Given("the following configuration:")

304

public void configuration(Properties config) {

305

// Automatically parsed from doc string to Properties object

306

String environment = config.getProperty("environment");

307

String database = config.getProperty("database.url");

308

309

applyConfiguration(config);

310

}

311

312

// Structured data from doc string

313

@Given("the API response:")

314

public void api_response(JsonNode response) {

315

// Automatically parsed from JSON doc string

316

String status = response.get("status").asText();

317

JsonNode data = response.get("data");

318

319

validateResponse(status, data);

320

}

321

}

322

```

323

324

### Advanced Data Table Features

325

326

Complex data table operations including transposition, custom transformations, and nested structures.

327

328

```java { .api }

329

/**

330

* Transposed data tables

331

*/

332

public void stepWithTransposedTable(@Transpose DataTable table) { }

333

public void stepWithTransposedObjects(@Transpose List<CustomObject> objects) { }

334

335

/**

336

* Nested data structures

337

*/

338

public void stepWithNestedMaps(Map<String, Map<String, Object>> nestedData) { }

339

public void stepWithNestedLists(List<List<List<String>>> nestedLists) { }

340

341

/**

342

* Custom replacement handling

343

*/

344

public void stepWithReplacements(List<CustomObject> objects) { }

345

// Uses @DataTableType(replaceWithEmptyString = {"[blank]", "null"})

346

```

347

348

**Usage Examples:**

349

350

```java

351

import io.cucumber.java.Transpose;

352

import io.cucumber.datatable.DataTable;

353

354

public class AdvancedDataTableExamples {

355

356

// Transposed table for vertical data layout

357

@Given("the user profile:")

358

public void user_profile(@Transpose DataTable profile) {

359

// Input table: | name | John Doe |

360

// | email | john@test.com |

361

// | age | 30 |

362

//

363

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

364

// | John Doe | john@test.com | 30 |

365

366

List<Map<String, String>> profileMaps = profile.asMaps();

367

Map<String, String> userInfo = profileMaps.get(0);

368

369

String name = userInfo.get("name");

370

String email = userInfo.get("email");

371

int age = Integer.parseInt(userInfo.get("age"));

372

}

373

374

// Complex nested structure

375

@Given("the department structure:")

376

public void department_structure(Map<String, Map<String, List<String>>> deptStructure) {

377

// Table with department -> role -> list of people

378

Map<String, List<String>> engineering = deptStructure.get("Engineering");

379

List<String> developers = engineering.get("Developer");

380

List<String> managers = engineering.get("Manager");

381

382

assignPeopleToDepartment("Engineering", "Developer", developers);

383

assignPeopleToDepartment("Engineering", "Manager", managers);

384

}

385

386

// Custom object with empty value handling

387

@Given("the product catalog:")

388

public void product_catalog(List<Product> products) {

389

// Uses @DataTableType with replaceWithEmptyString configuration

390

for (Product product : products) {

391

// Empty/null values automatically handled by transformer

392

if (product.getDescription() != null) {

393

processDescription(product.getDescription());

394

}

395

}

396

}

397

398

// Multi-dimensional data

399

@Given("the test matrix:")

400

public void test_matrix(List<List<TestCase>> testMatrix) {

401

for (int i = 0; i < testMatrix.size(); i++) {

402

List<TestCase> row = testMatrix.get(i);

403

for (int j = 0; j < row.size(); j++) {

404

TestCase testCase = row.get(j);

405

runTest(i, j, testCase);

406

}

407

}

408

}

409

410

// Dynamic column handling

411

@Given("the dynamic data:")

412

public void dynamic_data(DataTable table) {

413

List<String> headers = table.row(0);

414

415

for (int i = 1; i < table.height(); i++) {

416

Map<String, String> rowData = new HashMap<>();

417

List<String> row = table.row(i);

418

419

for (int j = 0; j < headers.size(); j++) {

420

String header = headers.get(j);

421

String value = j < row.size() ? row.get(j) : "";

422

rowData.put(header, value);

423

}

424

425

processDynamicRow(rowData);

426

}

427

}

428

}

429

```

430

431

### Error Handling and Validation

432

433

Robust error handling and validation for data table and doc string processing.

434

435

```java

436

public class DataValidationExamples {

437

438

@Given("the validated user data:")

439

public void validated_users(List<Map<String, String>> users) {

440

for (Map<String, String> user : users) {

441

// Validate required fields

442

validateRequired(user, "name", "email");

443

444

// Validate data formats

445

String email = user.get("email");

446

if (!isValidEmail(email)) {

447

throw new IllegalArgumentException("Invalid email: " + email);

448

}

449

450

// Validate numeric fields

451

String ageStr = user.get("age");

452

if (ageStr != null && !ageStr.isEmpty()) {

453

try {

454

int age = Integer.parseInt(ageStr);

455

if (age < 0 || age > 150) {

456

throw new IllegalArgumentException("Invalid age: " + age);

457

}

458

} catch (NumberFormatException e) {

459

throw new IllegalArgumentException("Age must be a number: " + ageStr);

460

}

461

}

462

}

463

}

464

465

@Given("the JSON configuration:")

466

public void json_config(String jsonString) {

467

try {

468

ObjectMapper mapper = new ObjectMapper();

469

JsonNode config = mapper.readTree(jsonString);

470

471

// Validate required configuration fields

472

if (!config.has("environment")) {

473

throw new IllegalArgumentException("Missing required field: environment");

474

}

475

476

// Process validated configuration

477

processConfiguration(config);

478

479

} catch (JsonProcessingException e) {

480

throw new IllegalArgumentException("Invalid JSON configuration: " + e.getMessage());

481

}

482

}

483

484

private void validateRequired(Map<String, String> data, String... fields) {

485

for (String field : fields) {

486

String value = data.get(field);

487

if (value == null || value.trim().isEmpty()) {

488

throw new IllegalArgumentException("Required field missing or empty: " + field);

489

}

490

}

491

}

492

493

private boolean isValidEmail(String email) {

494

return email != null && email.contains("@") && email.contains(".");

495

}

496

}

497

```