or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

custom-config.mddata-formats.mdindex.mdrunner-annotations.mdutilities.md

custom-config.mddocs/

0

# Custom Resolution and Configuration

1

2

This document covers advanced features including custom data provider method resolvers, test name formatting with placeholders, and filtering capabilities.

3

4

## Required Imports

5

6

```java

7

import com.tngtech.java.junit.dataprovider.DataProviderMethodResolver;

8

import com.tngtech.java.junit.dataprovider.internal.DefaultDataProviderMethodResolver;

9

import com.tngtech.java.junit.dataprovider.Placeholders;

10

import com.tngtech.java.junit.dataprovider.internal.placeholder.BasePlaceholder;

11

import org.junit.runners.model.FrameworkMethod;

12

import org.junit.runners.model.TestClass;

13

import java.lang.reflect.Method;

14

import java.util.List;

15

```

16

17

## Custom Data Provider Method Resolvers

18

19

The DataProviderMethodResolver interface allows you to implement custom logic for finding data provider methods.

20

21

```java { .api }

22

public interface DataProviderMethodResolver {

23

24

/**

25

* Returns dataprovider methods for the given test method.

26

* @param testMethod test method that uses a dataprovider

27

* @param useDataProvider UseDataProvider annotation on the test method

28

* @return resolved dataprovider methods or empty list if none found

29

* @throws IllegalArgumentException if testMethod is null

30

*/

31

List<FrameworkMethod> resolve(FrameworkMethod testMethod, UseDataProvider useDataProvider);

32

}

33

```

34

35

### DefaultDataProviderMethodResolver

36

37

The built-in resolver that implements convention-based method resolution:

38

39

```java { .api }

40

public class DefaultDataProviderMethodResolver implements DataProviderMethodResolver {

41

42

/**

43

* Resolve using convention-based strategies in this order:

44

* 1. Explicit name via UseDataProvider.value()

45

* 2. Method name equals test method name

46

* 3. Replace "test" prefix with "dataProvider" or "data"

47

* 4. Add "dataProvider" or "data" prefix to capitalized method name

48

* @param testMethod test method that uses a dataprovider

49

* @param useDataProvider UseDataProvider annotation on the test method

50

* @return resolved dataprovider methods or empty list if none found

51

*/

52

public List<FrameworkMethod> resolve(FrameworkMethod testMethod, UseDataProvider useDataProvider)

53

54

/**

55

* Find classes where data provider methods should be searched.

56

* @param testMethod test method context

57

* @param locations specified location classes, or test class if empty

58

* @return list of TestClass objects to search in

59

*/

60

protected List<TestClass> findDataProviderLocations(FrameworkMethod testMethod, Class<?>[] locations)

61

62

/**

63

* Search for data provider methods across multiple locations.

64

* @param locations TestClass objects to search in

65

* @param testMethodName name of the test method

66

* @param value explicit data provider name or DEFAULT_VALUE for convention

67

* @return list of matching data provider methods

68

*/

69

protected List<FrameworkMethod> findDataProviderMethods(List<TestClass> locations, String testMethodName, String value)

70

71

/**

72

* Find data provider method in a specific location using naming conventions.

73

* @param location TestClass to search in

74

* @param testMethodName name of the test method

75

* @param value explicit data provider name or DEFAULT_VALUE for convention

76

* @return matching data provider method or null if not found

77

*/

78

protected FrameworkMethod findDataProviderMethod(TestClass location, String testMethodName, String value)

79

}

80

```

81

82

### Custom Resolver Implementation

83

84

Create a custom resolver for specialized naming or lookup strategies:

85

86

```java

87

public class PrefixBasedResolver implements DataProviderMethodResolver {

88

89

@Override

90

public List<FrameworkMethod> resolve(FrameworkMethod testMethod, UseDataProvider useDataProvider) {

91

String testMethodName = testMethod.getName();

92

String dataProviderName;

93

94

if (!UseDataProvider.DEFAULT_VALUE.equals(useDataProvider.value())) {

95

dataProviderName = useDataProvider.value();

96

} else {

97

// Custom logic: look for methods with "dp_" prefix

98

dataProviderName = "dp_" + testMethodName;

99

}

100

101

TestClass testClass = new TestClass(testMethod.getMethod().getDeclaringClass());

102

List<FrameworkMethod> dataProviderMethods = testClass.getAnnotatedMethods(DataProvider.class);

103

104

for (FrameworkMethod method : dataProviderMethods) {

105

if (method.getName().equals(dataProviderName)) {

106

return Arrays.asList(method);

107

}

108

}

109

110

return Collections.emptyList();

111

}

112

}

113

114

// Usage

115

@Test

116

@UseDataProvider(resolver = PrefixBasedResolver.class)

117

public void testStringLength(String input, int expected) {

118

assertEquals(expected, input.length());

119

}

120

121

@DataProvider

122

public static Object[][] dp_testStringLength() { // Custom prefix

123

return new Object[][] { {"hello", 5}, {"world", 5} };

124

}

125

```

126

127

### Multiple Location Resolution

128

129

Use data providers from multiple classes:

130

131

```java

132

public class CommonDataProviders {

133

@DataProvider

134

public static Object[][] commonStringData() {

135

return new Object[][] { {"test", 4}, {"hello", 5} };

136

}

137

}

138

139

public class SpecialDataProviders {

140

@DataProvider

141

public static Object[][] specialStringData() {

142

return new Object[][] { {"special", 7}, {"unique", 6} };

143

}

144

}

145

146

@Test

147

@UseDataProvider(

148

value = "commonStringData",

149

location = { CommonDataProviders.class, SpecialDataProviders.class }

150

)

151

public void testWithMultipleLocations(String input, int length) {

152

assertEquals(length, input.length());

153

}

154

```

155

156

### Resolver Strategies

157

158

Control how multiple resolvers are used:

159

160

```java

161

@Test

162

@UseDataProvider(

163

resolver = { CustomResolver1.class, CustomResolver2.class },

164

resolveStrategy = ResolveStrategy.UNTIL_FIRST_MATCH // Default

165

)

166

public void testFirstMatch(Object data) {

167

// Uses data from first resolver that finds a match

168

}

169

170

@Test

171

@UseDataProvider(

172

resolver = { CustomResolver1.class, CustomResolver2.class },

173

resolveStrategy = ResolveStrategy.AGGREGATE_ALL_MATCHES

174

)

175

public void testAggregated(Object data) {

176

// Combines data from all resolvers that find matches

177

}

178

```

179

180

## Test Method Name Formatting

181

182

Customize how parameterized test names are displayed using format patterns and placeholders.

183

184

### Format Patterns

185

186

The @DataProvider annotation supports format patterns to control test method naming:

187

188

```java { .api }

189

@DataProvider(format = "%m[%i: %p[0..-1]]") // Default format

190

@DataProvider(format = "%m[%i]") // Method name with index only

191

@DataProvider(format = "%m: %p[0]") // Method name with first parameter

192

@DataProvider(format = "%c.%m(%p[0..1])") // Class.method(first two params)

193

```

194

195

### Available Placeholders

196

197

```java { .api }

198

// Class placeholders

199

%c // Simple class name (e.g., "DataProviderTest")

200

%cc // Canonical class name (e.g., "com.example.DataProviderTest")

201

202

// Method placeholders

203

%m // Simple method name (e.g., "testStringLength")

204

%cm // Complete method signature (e.g., "com.example.Test.testStringLength(java.lang.String)")

205

206

// Index placeholder

207

%i // Data provider index (0, 1, 2, ...)

208

209

// Parameter placeholders

210

%p[0] // First parameter

211

%p[-1] // Last parameter

212

%p[1..3] // Parameters 1 through 3

213

%p[0..-2] // All parameters except last

214

%p[0..-1] // All parameters

215

```

216

217

### Formatting Examples

218

219

```java

220

@DataProvider(format = "%m[%i: %p[0]]")

221

public static Object[][] stringLengthData() {

222

return new Object[][] {

223

{ "hello", 5 },

224

{ "world", 5 },

225

{ "", 0 }

226

};

227

}

228

// Test names: testStringLength[0: hello], testStringLength[1: world], testStringLength[2: ]

229

230

@DataProvider(format = "%c.%m: %p[0] -> %p[1]")

231

public static Object[][] conversionData() {

232

return new Object[][] {

233

{ "123", 123 },

234

{ "true", true }

235

};

236

}

237

// Test names: MyTest.testConversion: 123 -> 123, MyTest.testConversion: true -> true

238

239

@DataProvider(format = "%m(%p[0..1]) #%i")

240

public static Object[][] rangeData() {

241

return new Object[][] {

242

{ 1, 10, "range1" },

243

{ 20, 30, "range2" }

244

};

245

}

246

// Test names: testRange(1, 10) #0, testRange(20, 30) #1

247

```

248

249

## Custom Placeholders

250

251

Extend the placeholder system with custom formatting logic.

252

253

### Placeholders Utility Class

254

255

The Placeholders class manages the collection of placeholder implementations used for test method name formatting.

256

257

```java { .api }

258

public class Placeholders {

259

260

/**

261

* Get all registered placeholders. Returns modifiable list.

262

* @return all BasePlaceholder instances for format processing

263

*/

264

public static List<BasePlaceholder> all()

265

266

/**

267

* Reset to default placeholders. Clears all custom placeholders and

268

* restores the default set: %c, %cc, %m, %cm, %i, %p[x]

269

*/

270

public static void reset()

271

}

272

```

273

274

The default placeholders included are:

275

- CanonicalClassNamePlaceholder (%cc) - Full canonical class name

276

- CompleteMethodSignaturePlaceholder (%cm) - Complete method signature

277

- IndexPlaceholder (%i) - Data provider row index

278

- ParameterPlaceholder (%p[x]) - Parameter access with indexing

279

- SimpleClassNamePlaceholder (%c) - Simple class name

280

- SimpleMethodNamePlaceholder (%m) - Method name

281

282

### Creating Custom Placeholders

283

284

Implement BasePlaceholder for custom formatting:

285

286

```java { .api }

287

public abstract class BasePlaceholder {

288

289

/**

290

* Constructor requires placeholder regex pattern.

291

* @param placeholderRegex regular expression to match placeholder

292

*/

293

public BasePlaceholder(String placeholderRegex)

294

295

/**

296

* Set context for placeholder processing.

297

* @param method test method being processed

298

* @param idx data provider row index

299

* @param parameters test method parameters

300

*/

301

public void setContext(Method method, int idx, Object[] parameters)

302

303

/**

304

* Process format string, replacing placeholder occurrences.

305

* @param formatPattern input format pattern

306

* @return processed format string with replacements

307

*/

308

public String process(String formatPattern)

309

310

/**

311

* Generate replacement for found placeholder. Override this method.

312

* @param placeholder matched placeholder string

313

* @return replacement string for the placeholder

314

*/

315

protected abstract String getReplacementFor(String placeholder);

316

}

317

```

318

319

Example custom placeholder:

320

321

```java

322

public class TimestampPlaceholder extends BasePlaceholder {

323

private String timestamp;

324

325

public TimestampPlaceholder() {

326

super("%t"); // Regex pattern to match %t placeholder

327

}

328

329

@Override

330

public void setContext(Method method, int idx, Object[] parameters) {

331

this.timestamp = Instant.now().toString();

332

}

333

334

@Override

335

protected String getReplacementFor(String placeholder) {

336

return timestamp;

337

}

338

}

339

340

// Register custom placeholder

341

static {

342

Placeholders.all().add(0, new TimestampPlaceholder());

343

}

344

345

@DataProvider(format = "%m[%i] at %t")

346

public static Object[][] timestampedData() {

347

return new Object[][] { {"test1"}, {"test2"} };

348

}

349

// Test names: testMethod[0] at 2023-12-01T10:30:00Z, testMethod[1] at 2023-12-01T10:30:00Z

350

```

351

352

### Parameter Transformation Placeholder

353

354

```java

355

public class HashPlaceholder extends BasePlaceholder {

356

private Object[] parameters;

357

358

public HashPlaceholder() {

359

super("%hash"); // Regex pattern to match %hash placeholder

360

}

361

362

@Override

363

public void setContext(Method method, int idx, Object[] parameters) {

364

this.parameters = parameters;

365

}

366

367

@Override

368

protected String getReplacementFor(String placeholder) {

369

return String.valueOf(Arrays.hashCode(parameters));

370

}

371

}

372

373

@DataProvider(format = "%m[%hash]")

374

public static Object[][] hashedData() {

375

return new Object[][] {

376

{"param1", "param2"},

377

{"param3", "param4"}

378

};

379

}

380

// Test names: testMethod[12345], testMethod[67890] (hash values)

381

```

382

383

## Filtering and Test Selection

384

385

Control which tests run using JUnit filters enhanced for data provider support.

386

387

### DataProviderFilter

388

389

```java { .api }

390

public class DataProviderFilter extends Filter {

391

392

/**

393

* Create filter that supports data provider row filtering.

394

* @param filter original filter to wrap

395

*/

396

public DataProviderFilter(Filter filter)

397

398

/**

399

* Determine if test should run, supporting data provider syntax.

400

* @param description test description

401

* @return true if test should run

402

*/

403

public boolean shouldRun(Description description)

404

405

/**

406

* Get description of wrapped filter.

407

* @return filter description

408

*/

409

public String describe()

410

411

// Pattern constants for parsing test descriptions

412

static final Pattern DESCRIPTION_PATTERN;

413

static final Pattern GENEROUS_DESCRIPTION_PATTERN;

414

}

415

```

416

417

### Filter Usage

418

419

The DataProviderRunner automatically wraps filters to support data provider test selection:

420

421

```java

422

// These filtering patterns work with DataProviderFilter:

423

// "testMethod" - runs all data provider rows for testMethod

424

// "testMethod[0]" - runs only row 0 of testMethod data provider

425

// "testMethod[0: param1, param2]" - runs specific parameterized test

426

// "com.example.TestClass" - runs all tests in TestClass

427

```

428

429

### Programmatic Filtering

430

431

```java

432

@RunWith(DataProviderRunner.class)

433

public class FilteredTest {

434

435

@DataProvider

436

public static Object[][] allData() {

437

return new Object[][] {

438

{"test1", 1}, {"test2", 2}, {"test3", 3}

439

};

440

}

441

442

@Test

443

@UseDataProvider("allData")

444

public void testFiltered(String name, int value) {

445

// Only specific rows will run based on filter

446

}

447

}

448

449

// Run only second data row:

450

// mvn test -Dtest="FilteredTest#testFiltered[1]*"

451

```

452

453

### Custom Filter Integration

454

455

Integrate with custom JUnit filters:

456

457

```java

458

public class CustomTestFilter extends Filter {

459

@Override

460

public boolean shouldRun(Description description) {

461

// Custom filtering logic

462

return description.getDisplayName().contains("important");

463

}

464

465

@Override

466

public String describe() {

467

return "Custom filter for important tests";

468

}

469

}

470

471

// DataProviderRunner automatically wraps custom filters:

472

runner.filter(new CustomTestFilter());

473

// Becomes: runner.filter(new DataProviderFilter(new CustomTestFilter()));

474

```

475

476

## Integration with Test Frameworks

477

478

### JUnit Categories

479

480

Data provider tests work seamlessly with JUnit Categories:

481

482

```java

483

public interface SlowTests {}

484

public interface FastTests {}

485

486

@RunWith(DataProviderRunner.class)

487

@Category({FastTests.class})

488

public class CategorizedTest {

489

490

@DataProvider

491

public static Object[][] quickData() {

492

return new Object[][] { {"quick1"}, {"quick2"} };

493

}

494

495

@Test

496

@UseDataProvider("quickData")

497

@Category(SlowTests.class) // Override class-level category

498

public void testCategorized(String input) {

499

// Test logic

500

}

501

}

502

```

503

504

### Custom Runners and Extensions

505

506

Extend DataProviderRunner for specialized behavior:

507

508

```java

509

public class CustomDataProviderRunner extends DataProviderRunner {

510

511

public CustomDataProviderRunner(Class<?> clazz) throws InitializationError {

512

super(clazz);

513

}

514

515

@Override

516

protected void initializeHelpers() {

517

super.initializeHelpers();

518

// Customize data converter, test generator, or validator

519

this.dataConverter = new CustomDataConverter();

520

}

521

522

@Override

523

protected List<FrameworkMethod> computeTestMethods() {

524

List<FrameworkMethod> methods = super.computeTestMethods();

525

// Apply custom filtering or transformation

526

return methods.stream()

527

.filter(this::shouldIncludeMethod)

528

.collect(Collectors.toList());

529

}

530

531

private boolean shouldIncludeMethod(FrameworkMethod method) {

532

// Custom inclusion logic

533

return !method.getName().startsWith("skip");

534

}

535

}

536

```