You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Acolyte模拟JDBC驱动:如何测试事务(提交/回滚)行为?

嘿,我之前在Scala项目里用Acolyte测试JDBC事务逻辑时也遇到过类似的问题,给你分享几个实用的方案,帮你验证commit/rollback是否被正确调用:

方案1:用Acolyte原生Handler直接捕获事务调用

Acolyte的MockConnection自带了事务相关的回调钩子,不需要自己写复杂的包装类,直接设置withCommitHandler和withRollbackHandler就能跟踪调用状态:

import acolyte.jdbc.MockConnection
import scala.util.Try

// 测试成功场景下的commit调用
test("executeUnitOfWorkNoResult triggers commit on successful execution") {
  var commitInvoked = false
  var rollbackInvoked = false

  // 配置MockConnection的事务回调
  val mockConn = MockConnection.empty
    .withCommitHandler(_ => { commitInvoked = true; () })
    .withRollbackHandler(_ => { rollbackInvoked = true; () })

  // 执行你的UnitOfWork方法
  val result = executeUnitOfWorkNoResult(List("INSERT INTO users VALUES (1, 'Alice')"))(() => mockConn)

  // 断言结果和调用状态
  assert(result.isSuccess)
  assert(commitInvoked, "Commit should have been called after successful work")
  assert(!rollbackInvoked, "Rollback shouldn't be called in success case")
}

// 测试失败场景下的rollback调用
test("executeUnitOfWorkNoResult triggers rollback on failure") {
  var commitInvoked = false
  var rollbackInvoked = false

  // 创建会抛出异常的MockConnection,模拟SQL执行失败
  val failingConn = MockConnection.empty
    .withCommitHandler(_ => { commitInvoked = true; () })
    .withRollbackHandler(_ => { rollbackInvoked = true; () })
    // 让执行SQL时抛出异常
    .withStatementHandler(_ => throw new java.sql.SQLException("Test failure"))

  val result = executeUnitOfWorkNoResult(List("INSERT INTO users VALUES (1, 'Alice')"))(() => failingConn)

  assert(result.isFailure)
  assert(rollbackInvoked, "Rollback should have been called after work failed")
  assert(!commitInvoked, "Commit shouldn't be called in failure case")
}

这个方案最简洁,完全利用Acolyte的API,不需要额外的包装类。

方案2:自定义跟踪Connection(适合更复杂的场景)

如果需要跟踪更多JDBC操作(比如setAutoCommit、保存点等),可以自己写一个包装类,把Connection的所有事务相关方法都记录下来:

import java.sql.{Connection, SQLException, Statement}
import scala.collection.mutable.ListBuffer

class TrackingConnection(delegate: Connection) extends Connection {
  // 记录所有事务相关调用的日志
  val callLog: ListBuffer[String] = ListBuffer.empty

  override def commit(): Unit = {
    callLog += "commit"
    delegate.commit()
  }

  override def rollback(): Unit = {
    callLog += "rollback"
    delegate.rollback()
  }

  override def setAutoCommit(autoCommit: Boolean): Unit = {
    callLog += s"setAutoCommit($autoCommit)"
    delegate.setAutoCommit(autoCommit)
  }

  // 其他Connection方法全部委托给底层的delegate
  override def close(): Unit = delegate.close()
  override def createStatement(): Statement = delegate.createStatement()
  // ... 这里需要实现Connection接口的所有剩余方法,或者用动态代理简化(比如用ByteBuddy)
}

然后在测试里用这个包装类包裹Acolyte的MockConnection:

test("verify full transaction flow including setAutoCommit") {
  val mockConn = MockConnection.empty
  val trackingConn = new TrackingConnection(mockConn)

  val result = executeUnitOfWorkNoResult(List("INSERT ..."))(() => trackingConn)

  assert(result.isSuccess)
  // 断言事务流程:先关闭自动提交,执行SQL,然后commit
  assert(trackingConn.callLog.contains("setAutoCommit(false)"))
  assert(trackingConn.callLog.contains("commit"))
}

额外提示

如果你的executeUnitOfWorkNoResult方法里包含事务边界的处理(比如默认关闭autoCommit),一定要记得验证这一步,因为这是事务生效的前提。另外,如果你用ScalaTest或Specs2这类测试框架,可以用它们的匹配器让断言更易读,比如:

import org.scalatest.matchers.should.Matchers._

commitInvoked shouldBe true
trackingConn.callLog should contain("setAutoCommit(false)")

内容的提问来源于stack exchange,提问作者Flo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:12:58