Skip to content

DefaultPrepAndExpectedTestCase keeps reusing a closed connection after the pool or server drops it between test methods #962

Description

@jeffjensen

Summary

DefaultPrepAndExpectedTestCase with setCloseConnectionAfterTest(false), reused across test methods (a base-class / @DbUnitTestCase field, paired with a CachingConnectionProvider), pins the first IDatabaseConnection it acquires in a private field and never re-checks it. If that connection's underlying JDBC connection is closed between test methods — pool max-lifetime/reap, PGConnectionPoolDataSource issuing a fresh logical handle, a bounced application context, an idle-in-transaction server kill — every following test on that instance fails, and keeps failing.

Affected versions

3.4.0, 3.5.0, 3.5.1 — setCloseConnectionAfterTest was added in 3.4.0 (#800). The 3.6.0 row count check surfaces the failure one lifecycle step earlier (preTest() rather than setupData()) but is not the cause.

Root cause

getReusableConnection() returns the cached connection field whenever it is non-null. closeReusableConnection() deliberately keeps that field set — not closing it — when closeConnectionAfterTest is false, with no check that the kept connection is still open. CachingConnectionProvider's own isClosed() / isValid() liveness check, which exists to replace a dead connection transparently, is never reached because the provider is not consulted again.

Failure shape

  • preTest()captureRowCountBaseline()RowCountChecker.capture()QueryPerTableRowCounter.countRows()AbstractDatabaseConnection.getRowCount()Connection.createStatement() on the closed connection
  • then, as a suppressed exception, postTest(false)cleanupData()AbstractDatabaseTester.onTearDown()DeleteAllOperation.execute() on the same closed connection

Reproduction

  1. One DefaultPrepAndExpectedTestCase instance, closeConnectionAfterTest=false, backed by a DataSourceDatabaseTester + shared CachingConnectionProvider.
  2. Run a full runTest(...) cycle — a connection is acquired and pinned.
  3. Close that connection's underlying java.sql.Connection (simulating the pool/server dropping it).
  4. Run a second cycle on the same instance → SQLException from captureRowCountBaseline(), and again from cleanupData()'s tear down operation.

Expected

The reused instance notices the connection is closed and re-acquires from the tester, letting the CachingConnectionProvider hand back a live replacement — the "transparently replaces a dead connection" guarantee the provider already documents.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area: databaseIDatabaseConnection, DatabaseConfig, QueryDataSetjavaPull requests that update java code

Type

Projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions