or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration-overrides.mdcore-test-support.mddao-testing.mdindex.mdjunit5-extensions.mdresource-testing.md

dao-testing.mddocs/

0

# DAO Testing

1

2

Database testing utilities with automated Hibernate SessionFactory management, transaction handling, and H2 in-memory database setup. Essential for testing data access layers with isolated database environments.

3

4

## Capabilities

5

6

### DAOTest

7

8

Abstract base class providing comprehensive database testing environment with Hibernate SessionFactory management, transaction handling, and configurable database setup.

9

10

```java { .api }

11

/**

12

* Abstract base class for DAO testing with Hibernate support

13

*/

14

public abstract class DAOTest {

15

16

/**

17

* Get the Hibernate SessionFactory for database operations

18

* @return SessionFactory instance for creating sessions

19

*/

20

public SessionFactory getSessionFactory();

21

22

/**

23

* Execute code within a database transaction with return value

24

* Automatically handles transaction commit/rollback

25

* @param call Callable to execute within transaction

26

* @return Result of the callable execution

27

* @throws Exception if the callable execution fails

28

*/

29

public <T> T inTransaction(Callable<T> call) throws Exception;

30

31

/**

32

* Execute code within a database transaction without return value

33

* Automatically handles transaction commit/rollback

34

* @param action Runnable to execute within transaction

35

* @throws Exception if the action execution fails

36

*/

37

public void inTransaction(Runnable action) throws Exception;

38

39

// Lifecycle methods

40

/**

41

* Initialize the database testing environment before tests

42

* Sets up SessionFactory, creates schema, etc.

43

* @throws Exception if initialization fails

44

*/

45

public void before() throws Exception;

46

47

/**

48

* Cleanup the database testing environment after tests

49

* Closes SessionFactory, drops schema, etc.

50

* @throws Exception if cleanup fails

51

*/

52

public void after() throws Exception;

53

}

54

```

55

56

### DAOTest.Builder

57

58

Fluent builder for configuring DAOTest instances with database connection settings, Hibernate properties, entity classes, and custom configuration.

59

60

```java { .api }

61

/**

62

* Abstract builder for DAOTest configuration

63

* @param <B> Builder type for method chaining

64

*/

65

public static abstract class Builder<B extends Builder<B>> {

66

67

// Database connection configuration

68

/**

69

* Set the database URL

70

* @param url JDBC URL (default: H2 in-memory database)

71

* @return Builder instance for chaining

72

*/

73

public B setUrl(String url);

74

75

/**

76

* Set the database username

77

* @param username Database username (default: "sa")

78

* @return Builder instance for chaining

79

*/

80

public B setUsername(String username);

81

82

/**

83

* Set the database password

84

* @param password Database password (default: empty)

85

* @return Builder instance for chaining

86

*/

87

public B setPassword(String password);

88

89

// JDBC driver configuration

90

/**

91

* Set the JDBC driver class

92

* @param driver JDBC driver class (default: H2 driver)

93

* @return Builder instance for chaining

94

*/

95

public B setDriver(Class<? extends Driver> driver);

96

97

/**

98

* Set the JDBC driver class by name

99

* @param driver JDBC driver class name

100

* @return Builder instance for chaining

101

*/

102

public B setDriver(String driver);

103

104

// Hibernate configuration

105

/**

106

* Set Hibernate DDL auto mode

107

* @param hbm2ddlAuto DDL mode (create-drop, create, update, validate, none)

108

* @return Builder instance for chaining

109

*/

110

public B setHbm2DdlAuto(String hbm2ddlAuto);

111

112

/**

113

* Enable/disable SQL statement logging

114

* @param showSql true to log SQL statements, false otherwise

115

* @return Builder instance for chaining

116

*/

117

public B setShowSql(boolean showSql);

118

119

/**

120

* Enable/disable SQL comments in logged statements

121

* @param useSqlComments true to include comments, false otherwise

122

* @return Builder instance for chaining

123

*/

124

public B useSqlComments(boolean useSqlComments);

125

126

/**

127

* Enable/disable Logback logging bootstrap

128

* @param value true to bootstrap logging, false otherwise

129

* @return Builder instance for chaining

130

*/

131

public B bootstrapLogging(boolean value);

132

133

// Entity configuration

134

/**

135

* Add an entity class to the Hibernate configuration

136

* @param entityClass JPA entity class to register

137

* @return Builder instance for chaining

138

*/

139

public B addEntityClass(Class<?> entityClass);

140

141

// Custom properties

142

/**

143

* Set a custom Hibernate property

144

* @param key Property name

145

* @param value Property value

146

* @return Builder instance for chaining

147

*/

148

public B setProperty(String key, String value);

149

150

/**

151

* Add custom configuration logic

152

* @param configurationCustomizer Consumer for custom Configuration setup

153

* @return Builder instance for chaining

154

*/

155

public B customizeConfiguration(Consumer<Configuration> configurationCustomizer);

156

157

/**

158

* Build the configured DAOTest instance

159

* @return Configured DAOTest instance ready for testing

160

*/

161

public abstract DAOTest build();

162

}

163

```

164

165

**Usage Examples:**

166

167

```java

168

// Basic DAO testing with H2 in-memory database

169

DAOTest daoTest = DAOTestExtension.newBuilder()

170

.addEntityClass(User.class)

171

.addEntityClass(Order.class)

172

.build();

173

174

@BeforeEach

175

public void setUp() throws Exception {

176

daoTest.before();

177

}

178

179

@AfterEach

180

public void tearDown() throws Exception {

181

daoTest.after();

182

}

183

184

@Test

185

public void testUserDAO() throws Exception {

186

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

187

188

// Test data creation

189

User user = daoTest.inTransaction(() -> {

190

User newUser = new User("Alice", "alice@example.com");

191

return userDAO.save(newUser);

192

});

193

194

assertThat(user.getId()).isNotNull();

195

196

// Test data retrieval

197

Optional<User> foundUser = daoTest.inTransaction(() ->

198

userDAO.findById(user.getId())

199

);

200

201

assertThat(foundUser).isPresent();

202

assertThat(foundUser.get().getName()).isEqualTo("Alice");

203

}

204

205

// Custom database configuration

206

DAOTest daoTest = DAOTestExtension.newBuilder()

207

.setUrl("jdbc:postgresql://localhost:5432/test_db")

208

.setUsername("test_user")

209

.setPassword("test_password")

210

.setDriver("org.postgresql.Driver")

211

.addEntityClass(User.class)

212

.addEntityClass(Order.class)

213

.setHbm2DdlAuto("create-drop")

214

.setShowSql(true)

215

.build();

216

217

// Multiple entity classes and custom properties

218

DAOTest daoTest = DAOTestExtension.newBuilder()

219

.addEntityClass(User.class)

220

.addEntityClass(Order.class)

221

.addEntityClass(Product.class)

222

.addEntityClass(Category.class)

223

.setProperty("hibernate.cache.use_second_level_cache", "false")

224

.setProperty("hibernate.cache.use_query_cache", "false")

225

.setProperty("hibernate.jdbc.batch_size", "25")

226

.useSqlComments(true)

227

.build();

228

229

// Custom configuration with advanced Hibernate settings

230

DAOTest daoTest = DAOTestExtension.newBuilder()

231

.addEntityClass(User.class)

232

.customizeConfiguration(config -> {

233

config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");

234

config.setProperty("hibernate.connection.isolation", "2");

235

config.setProperty("hibernate.order_inserts", "true");

236

config.setProperty("hibernate.order_updates", "true");

237

})

238

.build();

239

240

// Testing with transaction rollback for data isolation

241

@Test

242

public void testTransactionRollback() throws Exception {

243

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

244

245

try {

246

daoTest.inTransaction(() -> {

247

User user = new User("Bob", "bob@example.com");

248

userDAO.save(user);

249

250

// Simulate error that causes rollback

251

throw new RuntimeException("Test rollback");

252

});

253

} catch (RuntimeException e) {

254

// Expected exception

255

}

256

257

// Verify rollback occurred

258

List<User> users = daoTest.inTransaction(() -> userDAO.findAll());

259

assertThat(users).isEmpty();

260

}

261

262

// Complex DAO testing with relationships

263

@Test

264

public void testUserOrderRelationship() throws Exception {

265

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

266

OrderDAO orderDAO = new OrderDAO(daoTest.getSessionFactory());

267

268

User user = daoTest.inTransaction(() -> {

269

User newUser = new User("Charlie", "charlie@example.com");

270

return userDAO.save(newUser);

271

});

272

273

Order order = daoTest.inTransaction(() -> {

274

Order newOrder = new Order(user, "Test Product", new BigDecimal("99.99"));

275

return orderDAO.save(newOrder);

276

});

277

278

// Test relationship loading

279

User userWithOrders = daoTest.inTransaction(() ->

280

userDAO.findByIdWithOrders(user.getId())

281

);

282

283

assertThat(userWithOrders.getOrders()).hasSize(1);

284

assertThat(userWithOrders.getOrders().get(0).getId()).isEqualTo(order.getId());

285

}

286

287

// Testing DAO queries and pagination

288

@Test

289

public void testDAOQueries() throws Exception {

290

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

291

292

// Create test data

293

daoTest.inTransaction(() -> {

294

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

295

User user = new User("User" + i, "user" + i + "@example.com");

296

userDAO.save(user);

297

}

298

});

299

300

// Test pagination

301

List<User> firstPage = daoTest.inTransaction(() ->

302

userDAO.findAll(0, 5)

303

);

304

assertThat(firstPage).hasSize(5);

305

306

List<User> secondPage = daoTest.inTransaction(() ->

307

userDAO.findAll(5, 5)

308

);

309

assertThat(secondPage).hasSize(5);

310

311

// Test search queries

312

List<User> searchResults = daoTest.inTransaction(() ->

313

userDAO.findByEmailContaining("user1")

314

);

315

assertThat(searchResults).hasSize(2); // user1 and user10

316

}

317

```

318

319

## Common DAO Testing Patterns

320

321

### Entity Lifecycle Testing

322

323

```java

324

@Test

325

public void testEntityLifecycle() throws Exception {

326

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

327

328

// Create

329

User user = daoTest.inTransaction(() -> {

330

User newUser = new User("Dave", "dave@example.com");

331

return userDAO.save(newUser);

332

});

333

assertThat(user.getId()).isNotNull();

334

335

// Read

336

Optional<User> foundUser = daoTest.inTransaction(() ->

337

userDAO.findById(user.getId())

338

);

339

assertThat(foundUser).isPresent();

340

341

// Update

342

daoTest.inTransaction(() -> {

343

User toUpdate = userDAO.findById(user.getId()).orElseThrow();

344

toUpdate.setEmail("dave.updated@example.com");

345

userDAO.save(toUpdate);

346

});

347

348

User updatedUser = daoTest.inTransaction(() ->

349

userDAO.findById(user.getId()).orElseThrow()

350

);

351

assertThat(updatedUser.getEmail()).isEqualTo("dave.updated@example.com");

352

353

// Delete

354

daoTest.inTransaction(() -> {

355

userDAO.delete(user.getId());

356

});

357

358

Optional<User> deletedUser = daoTest.inTransaction(() ->

359

userDAO.findById(user.getId())

360

);

361

assertThat(deletedUser).isEmpty();

362

}

363

```

364

365

### Query Performance Testing

366

367

```java

368

@Test

369

public void testQueryPerformance() throws Exception {

370

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

371

372

// Create large dataset

373

daoTest.inTransaction(() -> {

374

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

375

User user = new User("User" + i, "user" + i + "@example.com");

376

userDAO.save(user);

377

}

378

});

379

380

// Test query performance

381

long startTime = System.currentTimeMillis();

382

383

List<User> results = daoTest.inTransaction(() ->

384

userDAO.findActiveUsersByDomain("example.com")

385

);

386

387

long endTime = System.currentTimeMillis();

388

long duration = endTime - startTime;

389

390

assertThat(results).isNotEmpty();

391

assertThat(duration).isLessThan(1000); // Should complete within 1 second

392

}

393

```

394

395

### Constraint Violation Testing

396

397

```java

398

@Test

399

public void testUniqueConstraintViolation() {

400

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

401

402

assertThatThrownBy(() -> {

403

daoTest.inTransaction(() -> {

404

// Create first user

405

User user1 = new User("Alice", "alice@example.com");

406

userDAO.save(user1);

407

408

// Try to create second user with same email (should violate unique constraint)

409

User user2 = new User("Alice2", "alice@example.com");

410

userDAO.save(user2);

411

});

412

}).hasCauseInstanceOf(ConstraintViolationException.class);

413

}

414

```

415

416

### Transaction Isolation Testing

417

418

```java

419

@Test

420

public void testTransactionIsolation() throws Exception {

421

UserDAO userDAO = new UserDAO(daoTest.getSessionFactory());

422

423

// Create initial data

424

User user = daoTest.inTransaction(() -> {

425

User newUser = new User("Eve", "eve@example.com");

426

return userDAO.save(newUser);

427

});

428

429

// Simulate concurrent modification

430

CountDownLatch latch = new CountDownLatch(2);

431

AtomicReference<Exception> exception = new AtomicReference<>();

432

433

// Transaction 1: Update user

434

CompletableFuture.runAsync(() -> {

435

try {

436

daoTest.inTransaction(() -> {

437

User toUpdate = userDAO.findById(user.getId()).orElseThrow();

438

toUpdate.setName("Eve Updated");

439

Thread.sleep(100); // Simulate processing time

440

userDAO.save(toUpdate);

441

});

442

} catch (Exception e) {

443

exception.set(e);

444

} finally {

445

latch.countDown();

446

}

447

});

448

449

// Transaction 2: Concurrent read

450

CompletableFuture.runAsync(() -> {

451

try {

452

User readUser = daoTest.inTransaction(() ->

453

userDAO.findById(user.getId()).orElseThrow()

454

);

455

// Should read original value due to isolation

456

assertThat(readUser.getName()).isEqualTo("Eve");

457

} catch (Exception e) {

458

exception.set(e);

459

} finally {

460

latch.countDown();

461

}

462

});

463

464

latch.await(5, TimeUnit.SECONDS);

465

466

if (exception.get() != null) {

467

throw exception.get();

468

}

469

}

470

```