or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

assertions-matchers.mdcluster-management.mdcore-testing.mddata-generation.mdindex.mdmock-implementations.mdspecialized-testing.md

assertions-matchers.mddocs/

0

# Assertions and Matchers

1

2

The Elasticsearch test framework provides extensive assertion utilities and custom Hamcrest matchers for validating search results, cluster state, responses, and other Elasticsearch-specific data structures. These utilities enable expressive and maintainable test assertions.

3

4

## ElasticsearchAssertions

5

6

The core assertion utility class providing high-level assertions for Elasticsearch operations and responses.

7

8

```{ .api }

9

package org.elasticsearch.test.hamcrest;

10

11

import org.elasticsearch.action.ActionFuture;

12

import org.elasticsearch.action.ActionResponse;

13

import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;

14

import org.elasticsearch.action.bulk.BulkResponse;

15

import org.elasticsearch.action.get.GetResponse;

16

import org.elasticsearch.action.search.SearchResponse;

17

import org.elasticsearch.cluster.health.ClusterHealthStatus;

18

import org.elasticsearch.common.bytes.BytesReference;

19

import org.elasticsearch.rest.RestStatus;

20

import org.elasticsearch.search.SearchHit;

21

import org.hamcrest.Matcher;

22

23

/**

24

* Static utility methods for creating Elasticsearch-specific assertions and matchers.

25

* Provides fluent, readable assertions for common Elasticsearch testing scenarios.

26

*/

27

public class ElasticsearchAssertions {

28

29

/**

30

* Asserts that an ActionFuture completes successfully within the default timeout.

31

*

32

* @param future the future to wait for

33

* @param <T> response type

34

* @return the response from the future

35

* @throws AssertionError if future fails or times out

36

*/

37

public static <T extends ActionResponse> T assertAcked(ActionFuture<T> future);

38

39

/**

40

* Asserts that a response indicates an acknowledged operation.

41

*

42

* @param response response to check

43

* @throws AssertionError if operation was not acknowledged

44

*/

45

public static void assertAcked(IsAcknowledgedSupplier response);

46

47

/**

48

* Asserts that a bulk response has no failures.

49

*

50

* @param response bulk response to check

51

* @throws AssertionError if any items in the bulk response failed

52

*/

53

public static void assertNoFailures(BulkResponse response);

54

55

/**

56

* Asserts that a search response completed successfully with no failures.

57

*

58

* @param response search response to check

59

* @throws AssertionError if search had shard failures or timed out

60

*/

61

public static void assertNoFailures(SearchResponse response);

62

63

/**

64

* Asserts that a cluster health response indicates the expected health status.

65

*

66

* @param response cluster health response

67

* @param expectedStatus expected health status

68

* @throws AssertionError if health status doesn't match

69

*/

70

public static void assertHealthStatus(ClusterHealthResponse response,

71

ClusterHealthStatus expectedStatus);

72

73

/**

74

* Asserts that a search response contains the expected number of hits.

75

*

76

* @param response search response to check

77

* @param expectedHits expected total hit count

78

* @throws AssertionError if hit count doesn't match

79

*/

80

public static void assertHitCount(SearchResponse response, long expectedHits);

81

82

/**

83

* Asserts that a search response contains exactly one hit.

84

*

85

* @param response search response to check

86

* @throws AssertionError if hit count is not exactly 1

87

*/

88

public static void assertSingleHit(SearchResponse response);

89

90

/**

91

* Asserts that a search response contains no hits.

92

*

93

* @param response search response to check

94

* @throws AssertionError if any hits are found

95

*/

96

public static void assertNoSearchHits(SearchResponse response);

97

98

/**

99

* Asserts that search hits are ordered according to the specified sort criteria.

100

*

101

* @param response search response to check

102

* @param sortField field used for sorting

103

* @param ascending true if sort should be ascending, false for descending

104

* @throws AssertionError if hits are not properly sorted

105

*/

106

public static void assertOrderedSearchHits(SearchResponse response,

107

String sortField,

108

boolean ascending);

109

110

/**

111

* Asserts that a SearchHit contains a field with the expected value.

112

*

113

* @param hit search hit to check

114

* @param field field name to check

115

* @param expectedValue expected field value

116

* @throws AssertionError if field value doesn't match

117

*/

118

public static void assertSearchHitHasField(SearchHit hit, String field, Object expectedValue);

119

120

/**

121

* Asserts that a GetResponse indicates a document exists.

122

*

123

* @param response get response to check

124

* @throws AssertionError if document doesn't exist

125

*/

126

public static void assertDocumentExists(GetResponse response);

127

128

/**

129

* Asserts that a GetResponse indicates a document does not exist.

130

*

131

* @param response get response to check

132

* @throws AssertionError if document exists

133

*/

134

public static void assertDocumentMissing(GetResponse response);

135

136

/**

137

* Waits for and asserts cluster health reaches the specified status.

138

*

139

* @param client client to use for health checks

140

* @param status expected health status

141

* @param indices indices to check (empty for all indices)

142

*/

143

public static void ensureHealth(Client client,

144

ClusterHealthStatus status,

145

String... indices);

146

147

/**

148

* Waits for cluster to reach green health status.

149

*

150

* @param client client to use for health checks

151

* @param indices indices to check (empty for all indices)

152

*/

153

public static void ensureGreen(Client client, String... indices);

154

155

/**

156

* Waits for cluster to reach yellow or green health status.

157

*

158

* @param client client to use for health checks

159

* @param indices indices to check (empty for all indices)

160

*/

161

public static void ensureYellow(Client client, String... indices);

162

163

/**

164

* Asserts that two BytesReference objects are equal.

165

*

166

* @param expected expected bytes

167

* @param actual actual bytes

168

* @throws AssertionError if bytes don't match

169

*/

170

public static void assertBytesEqual(BytesReference expected, BytesReference actual);

171

172

/**

173

* Asserts that an exception was caused by a specific root cause.

174

*

175

* @param exception exception to examine

176

* @param expectedCause expected root cause class

177

* @throws AssertionError if root cause doesn't match

178

*/

179

public static void assertCausedBy(Throwable exception, Class<? extends Throwable> expectedCause);

180

}

181

```

182

183

## Custom Hamcrest Matchers

184

185

### Search Response Matchers

186

187

```{ .api }

188

package org.elasticsearch.test.hamcrest;

189

190

import org.elasticsearch.action.search.SearchResponse;

191

import org.elasticsearch.search.SearchHit;

192

import org.hamcrest.Matcher;

193

194

/**

195

* Custom Hamcrest matchers for SearchResponse validation.

196

*/

197

public class SearchMatchers {

198

199

/**

200

* Matches SearchResponse with expected hit count.

201

*

202

* @param expectedCount expected number of hits

203

* @return matcher for hit count

204

*/

205

public static Matcher<SearchResponse> hasHitCount(long expectedCount);

206

207

/**

208

* Matches SearchResponse with hit count in specified range.

209

*

210

* @param min minimum hit count (inclusive)

211

* @param max maximum hit count (inclusive)

212

* @return matcher for hit count range

213

*/

214

public static Matcher<SearchResponse> hasHitCount(long min, long max);

215

216

/**

217

* Matches SearchResponse with no search failures.

218

*

219

* @return matcher for successful search

220

*/

221

public static Matcher<SearchResponse> isSuccessfulSearchResponse();

222

223

/**

224

* Matches SearchResponse that completed without timeout.

225

*

226

* @return matcher for non-timed-out search

227

*/

228

public static Matcher<SearchResponse> searchNotTimedOut();

229

230

/**

231

* Matches SearchResponse containing a hit with specified ID.

232

*

233

* @param id document ID to match

234

* @return matcher for document ID

235

*/

236

public static Matcher<SearchResponse> hasHitWithId(String id);

237

238

/**

239

* Matches SearchResponse where hits are ordered by specified field.

240

*

241

* @param field field name used for ordering

242

* @param ascending true for ascending order, false for descending

243

* @return matcher for search hit ordering

244

*/

245

public static Matcher<SearchResponse> orderedBy(String field, boolean ascending);

246

247

/**

248

* Matches SearchHit containing a field with the expected value.

249

*

250

* @param fieldName field name to check

251

* @param expectedValue expected field value

252

* @return matcher for search hit field

253

*/

254

public static Matcher<SearchHit> hasField(String fieldName, Object expectedValue);

255

256

/**

257

* Matches SearchHit with expected document ID.

258

*

259

* @param expectedId expected document ID

260

* @return matcher for document ID

261

*/

262

public static Matcher<SearchHit> hasId(String expectedId);

263

264

/**

265

* Matches SearchHit with expected document type.

266

*

267

* @param expectedType expected document type

268

* @return matcher for document type

269

*/

270

public static Matcher<SearchHit> hasType(String expectedType);

271

272

/**

273

* Matches SearchHit with expected index name.

274

*

275

* @param expectedIndex expected index name

276

* @return matcher for index name

277

*/

278

public static Matcher<SearchHit> hasIndex(String expectedIndex);

279

280

/**

281

* Matches SearchHit with score greater than or equal to minimum.

282

*

283

* @param minScore minimum score threshold

284

* @return matcher for minimum score

285

*/

286

public static Matcher<SearchHit> hasScore(float minScore);

287

288

/**

289

* Matches SearchHit containing highlighted fields.

290

*

291

* @param fieldName field that should be highlighted

292

* @return matcher for highlighted field

293

*/

294

public static Matcher<SearchHit> hasHighlighting(String fieldName);

295

}

296

```

297

298

### Cluster Health Matchers

299

300

```{ .api }

301

package org.elasticsearch.test.hamcrest;

302

303

import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;

304

import org.elasticsearch.cluster.health.ClusterHealthStatus;

305

import org.hamcrest.Matcher;

306

307

/**

308

* Hamcrest matchers for cluster health assertions.

309

*/

310

public class ClusterHealthMatchers {

311

312

/**

313

* Matches ClusterHealthResponse with expected status.

314

*

315

* @param expectedStatus expected cluster health status

316

* @return matcher for health status

317

*/

318

public static Matcher<ClusterHealthResponse> hasStatus(ClusterHealthStatus expectedStatus);

319

320

/**

321

* Matches ClusterHealthResponse indicating cluster is not timed out.

322

*

323

* @return matcher for non-timed-out cluster

324

*/

325

public static Matcher<ClusterHealthResponse> isNotTimedOut();

326

327

/**

328

* Matches ClusterHealthResponse with expected number of nodes.

329

*

330

* @param expectedNodes expected node count

331

* @return matcher for node count

332

*/

333

public static Matcher<ClusterHealthResponse> hasNodes(int expectedNodes);

334

335

/**

336

* Matches ClusterHealthResponse with expected number of data nodes.

337

*

338

* @param expectedDataNodes expected data node count

339

* @return matcher for data node count

340

*/

341

public static Matcher<ClusterHealthResponse> hasDataNodes(int expectedDataNodes);

342

343

/**

344

* Matches ClusterHealthResponse with no relocating shards.

345

*

346

* @return matcher for no relocating shards

347

*/

348

public static Matcher<ClusterHealthResponse> hasNoRelocatingShards();

349

350

/**

351

* Matches ClusterHealthResponse with no initializing shards.

352

*

353

* @return matcher for no initializing shards

354

*/

355

public static Matcher<ClusterHealthResponse> hasNoInitializingShards();

356

357

/**

358

* Matches ClusterHealthResponse with no unassigned shards.

359

*

360

* @return matcher for no unassigned shards

361

*/

362

public static Matcher<ClusterHealthResponse> hasNoUnassignedShards();

363

}

364

```

365

366

### Optional and Collection Matchers

367

368

```{ .api }

369

package org.elasticsearch.test.hamcrest;

370

371

import java.util.Collection;

372

import java.util.Optional;

373

import org.hamcrest.Matcher;

374

375

/**

376

* Hamcrest matchers for Optional and Collection types.

377

*/

378

public class OptionalMatchers {

379

380

/**

381

* Matches Optional that is present.

382

*

383

* @param <T> type of Optional content

384

* @return matcher for present Optional

385

*/

386

public static <T> Matcher<Optional<T>> isPresent();

387

388

/**

389

* Matches Optional that is present and contains the expected value.

390

*

391

* @param expectedValue expected Optional content

392

* @param <T> type of Optional content

393

* @return matcher for Optional with specific value

394

*/

395

public static <T> Matcher<Optional<T>> isPresentWith(T expectedValue);

396

397

/**

398

* Matches Optional that is empty.

399

*

400

* @param <T> type of Optional content

401

* @return matcher for empty Optional

402

*/

403

public static <T> Matcher<Optional<T>> isEmpty();

404

405

/**

406

* Matches Collection containing all specified items.

407

*

408

* @param items items that should be present

409

* @param <T> type of Collection elements

410

* @return matcher for Collection containing items

411

*/

412

public static <T> Matcher<Collection<T>> containsAll(T... items);

413

414

/**

415

* Matches Collection with expected size.

416

*

417

* @param expectedSize expected collection size

418

* @param <T> type of Collection elements

419

* @return matcher for Collection size

420

*/

421

public static <T> Matcher<Collection<T>> hasSize(int expectedSize);

422

423

/**

424

* Matches empty Collection.

425

*

426

* @param <T> type of Collection elements

427

* @return matcher for empty Collection

428

*/

429

public static <T> Matcher<Collection<T>> emptyCollection();

430

}

431

```

432

433

### Tuple and Rectangle Matchers

434

435

```{ .api }

436

package org.elasticsearch.test.hamcrest;

437

438

import org.elasticsearch.common.geo.GeoPoint;

439

import org.elasticsearch.geometry.Rectangle;

440

import org.elasticsearch.common.collect.Tuple;

441

import org.hamcrest.Matcher;

442

443

/**

444

* Specialized matchers for geometric and tuple data structures.

445

*/

446

public class TupleMatchers {

447

448

/**

449

* Matches Tuple with expected first element.

450

*

451

* @param expectedFirst expected first tuple element

452

* @param <T1> type of first element

453

* @param <T2> type of second element

454

* @return matcher for first tuple element

455

*/

456

public static <T1, T2> Matcher<Tuple<T1, T2>> hasFirst(T1 expectedFirst);

457

458

/**

459

* Matches Tuple with expected second element.

460

*

461

* @param expectedSecond expected second tuple element

462

* @param <T1> type of first element

463

* @param <T2> type of second element

464

* @return matcher for second tuple element

465

*/

466

public static <T1, T2> Matcher<Tuple<T1, T2>> hasSecond(T2 expectedSecond);

467

468

/**

469

* Matches Tuple with both expected elements.

470

*

471

* @param expectedFirst expected first element

472

* @param expectedSecond expected second element

473

* @param <T1> type of first element

474

* @param <T2> type of second element

475

* @return matcher for complete tuple

476

*/

477

public static <T1, T2> Matcher<Tuple<T1, T2>> isTuple(T1 expectedFirst, T2 expectedSecond);

478

}

479

480

/**

481

* Matchers for geometric Rectangle objects.

482

*/

483

public class RectangleMatchers {

484

485

/**

486

* Matches Rectangle containing the specified point.

487

*

488

* @param point point that should be contained

489

* @return matcher for point containment

490

*/

491

public static Matcher<Rectangle> containsPoint(GeoPoint point);

492

493

/**

494

* Matches Rectangle with expected bounds.

495

*

496

* @param minLon minimum longitude

497

* @param maxLon maximum longitude

498

* @param minLat minimum latitude

499

* @param maxLat maximum latitude

500

* @return matcher for rectangle bounds

501

*/

502

public static Matcher<Rectangle> hasBounds(double minLon, double maxLon,

503

double minLat, double maxLat);

504

505

/**

506

* Matches Rectangle that intersects with another rectangle.

507

*

508

* @param other rectangle to check intersection with

509

* @return matcher for rectangle intersection

510

*/

511

public static Matcher<Rectangle> intersects(Rectangle other);

512

}

513

```

514

515

## Usage Examples

516

517

### Search Assertion Examples

518

519

```java

520

import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;

521

import static org.elasticsearch.test.hamcrest.SearchMatchers.*;

522

523

public class SearchAssertionTest extends ESIntegTestCase {

524

525

public void testSearchAssertions() {

526

createIndex("test-index");

527

528

// Index test documents

529

client().prepareIndex("test-index")

530

.setId("1")

531

.setSource("title", "First Document", "score", 85)

532

.get();

533

client().prepareIndex("test-index")

534

.setId("2")

535

.setSource("title", "Second Document", "score", 92)

536

.get();

537

538

refresh("test-index");

539

540

// Search and use assertions

541

SearchResponse response = client().prepareSearch("test-index")

542

.setQuery(QueryBuilders.matchAllQuery())

543

.addSort("score", SortOrder.DESC)

544

.get();

545

546

// Basic assertions

547

assertNoFailures(response);

548

assertHitCount(response, 2);

549

550

// Hamcrest matchers

551

assertThat(response, hasHitCount(2));

552

assertThat(response, isSuccessfulSearchResponse());

553

assertThat(response, hasHitWithId("1"));

554

assertThat(response, orderedBy("score", false)); // descending

555

556

// Individual hit assertions

557

SearchHit firstHit = response.getHits().getHits()[0];

558

assertThat(firstHit, hasId("2")); // Higher score document first

559

assertThat(firstHit, hasField("title", "Second Document"));

560

assertThat(firstHit, hasScore(0.0f)); // Match all query has score

561

562

// Combined assertions

563

assertSingleHit(client().prepareSearch("test-index")

564

.setQuery(QueryBuilders.termQuery("title", "first"))

565

.get());

566

}

567

568

public void testClusterHealthAssertions() {

569

createIndex("health-test");

570

571

// Wait for and assert cluster health

572

ensureGreen("health-test");

573

574

// Manual health check with assertions

575

ClusterHealthResponse health = client().admin().cluster()

576

.prepareHealth("health-test")

577

.get();

578

579

assertHealthStatus(health, ClusterHealthStatus.GREEN);

580

assertThat(health, hasStatus(ClusterHealthStatus.GREEN));

581

assertThat(health, isNotTimedOut());

582

assertThat(health, hasNoUnassignedShards());

583

assertThat(health, hasDataNodes(numDataNodes()));

584

}

585

586

public void testBulkOperationAssertions() {

587

createIndex("bulk-test");

588

589

BulkRequestBuilder bulk = client().prepareBulk();

590

for (int i = 0; i < 10; i++) {

591

bulk.add(client().prepareIndex("bulk-test")

592

.setId(String.valueOf(i))

593

.setSource("field", "value" + i));

594

}

595

596

BulkResponse bulkResponse = bulk.get();

597

598

// Assert no bulk failures

599

assertNoFailures(bulkResponse);

600

assertThat("All items should succeed",

601

bulkResponse.hasFailures(), equalTo(false));

602

assertThat("Should have 10 items",

603

bulkResponse.getItems().length, equalTo(10));

604

}

605

}

606

```

607

608

### Custom Matcher Usage

609

610

```java

611

import static org.elasticsearch.test.hamcrest.OptionalMatchers.*;

612

import static org.elasticsearch.test.hamcrest.TupleMatchers.*;

613

614

public class CustomMatcherTest extends ESTestCase {

615

616

public void testOptionalMatchers() {

617

Optional<String> presentValue = Optional.of("test");

618

Optional<String> emptyValue = Optional.empty();

619

620

assertThat(presentValue, isPresent());

621

assertThat(presentValue, isPresentWith("test"));

622

assertThat(emptyValue, isEmpty());

623

}

624

625

public void testTupleMatchers() {

626

Tuple<String, Integer> tuple = new Tuple<>("hello", 42);

627

628

assertThat(tuple, hasFirst("hello"));

629

assertThat(tuple, hasSecond(42));

630

assertThat(tuple, isTuple("hello", 42));

631

}

632

633

public void testCollectionMatchers() {

634

List<String> items = Arrays.asList("a", "b", "c");

635

636

assertThat(items, hasSize(3));

637

assertThat(items, containsAll("a", "c"));

638

assertThat(Collections.emptyList(), emptyCollection());

639

}

640

}

641

```

642

643

### Exception and Error Assertion Examples

644

645

```java

646

public class ExceptionAssertionTest extends ESTestCase {

647

648

public void testExceptionAssertions() {

649

// Test expected exceptions with root cause checking

650

ElasticsearchException ex = expectThrows(ElasticsearchException.class, () -> {

651

throw new ElasticsearchException("Test error",

652

new IllegalArgumentException("Root cause"));

653

});

654

655

assertCausedBy(ex, IllegalArgumentException.class);

656

assertThat(ex.getMessage(), containsString("Test error"));

657

}

658

659

public void testSearchFailureAssertions() {

660

createIndex("test-index");

661

662

// Test search with invalid query

663

SearchPhaseExecutionException ex = expectThrows(

664

SearchPhaseExecutionException.class,

665

() -> client().prepareSearch("test-index")

666

.setQuery(QueryBuilders.scriptQuery(

667

new Script("invalid script that will fail")))

668

.get()

669

);

670

671

assertThat("Should have shard failures",

672

ex.shardFailures().length, greaterThan(0));

673

}

674

}

675

```

676

677

## Best Practices

678

679

### Assertion Selection

680

- Use `ElasticsearchAssertions` static methods for common scenarios

681

- Prefer Hamcrest matchers for complex or reusable assertions

682

- Combine basic assertions with custom matchers for comprehensive validation

683

684

### Readability

685

- Use descriptive matcher names that clearly express intent

686

- Chain matchers with `allOf()` and `anyOf()` for complex conditions

687

- Include meaningful assertion messages for better test failure diagnostics

688

689

### Performance

690

- Avoid overly complex matcher chains that impact test performance

691

- Use targeted assertions rather than comprehensive validation when appropriate

692

- Cache frequently used matchers in test utilities

693

694

### Maintainability

695

- Create custom matchers for domain-specific assertion patterns

696

- Extract common assertion patterns into utility methods

697

- Keep assertion logic close to the test scenarios that use them

698

699

The assertion and matcher utilities provide a rich vocabulary for expressing test expectations clearly and maintaining readable, reliable test code.