java - 使用 try-with-resource 时调用断言 close

标签 java unit-testing mocking jmock

我正在尝试为一些打开数据库连接并对数据库执行一些操作的代码编写单元测试。我想断言连接已正确关闭,即使抛出异常也是如此。

我想要测试的代码如下所示:

public void methodToTest(final String aName) {
  final String sqlDeleteStatement = "DELETE FROM " + DB_FULL_TABLE + " WHERE name=?";

  try (final Connection connection = this.dataSource.getConnection();
      final PreparedStatement deleteStatement = connection.prepareStatement(sqlDeleteStatement);) {
    connection.setAutoCommit(true);

    deleteStatement.setString(1, aName);
    deleteStatement.executeUpdate();
  }
  catch (final SQLException e) {
    // handle error
  }
}

我目前正在使用 jMock 为数据源和连接对象创建模拟实例,并模拟在 connection.prepareStatement 中抛出异常:

public void testConnectionClosed() throws Exception {
  final Mockery mockery = new Mockery();
  final DataSource dataSource = mockery.mock(DataSource.class);
  final Connection connection = mockery.mock(Connection.class);

  final String exceptionMessage = "intentionally thrown " + UUID.randomUUID();

  mockery.checking(new Expectations() {{
      oneOf(dataSource).getConnection();
      will(returnValue(connection));

      oneOf(connection).prepareStatement(with(any(String.class)));
      will(throwException(new SQLException(exceptionMessage)));

      oneOf(connection).close();
    }});

  final ClassUnderTest cut = new ClassUnderTest(dataSource);
  cut.methodToTest("someName");

  mockery.assertIsSatisfied();
}

我面临的问题是,测试是绿色的,并且没有connection.close()的期望。没有预期,我看到一个被抑制的 org.jmock.api.ExcpectationError:

Suppressed: unexpected invocation: java.sql.Connection1370903230.close()

但是测试并没有失败,因为错误是在 try-with-resource 语句的隐式 finally block 内引发的。

我不想仅仅为了使这个测试有意义而重写代码,但我也想确保正确的资源处理,而不需要太多关于非常具体的实现细节的知识,例如 try-with-resource 的使用。

有没有办法用 jMock 来实现这一点?


不,我并不是要求推荐图书馆。因此,请不要标记为偏离主题。

最佳答案

这里的关键点:您不必验证 try-with-resources 是否按照 Java 语言规范所说的那样工作。如果您找不到适用于 jMock 的此问题的解决方案 - 那么请寻找“下一个最好的方法”。那就是:为“良好路径”编写一个测试用例,以确保连接关闭。

含义:您的问题是该测试抛出异常,这意味着关闭调用对您“隐藏”。但是,当您编写抛出异常的测试时,您应该能够验证是否调用了close()

当您使用 try-with-resources 时,您可以推断它也会因错误路径而被调用。当然,这不太优雅。但这是一个务实的解决方案 - Root 于您正在使用“模糊”模拟框架的事实!

因此,真正的答案是:使用合理的模拟框架。

这在某种程度上是固执己见的,但 jmock 对于生产使用来说“不合理”——仅仅是因为“没有人”在使用它。而且这似乎是一个“几乎死掉”的项目。当你转向jmock site您很容易遇到“死”链接。当你求助于他们的 github存在 - 自 2016 年以来,您发现只有少数提交。无论如何,只有少数提交者,并且提交数量在很长一段时间内非常接近于零。

当您依靠开源工具来支持您的项目/产品时,您希望确保有一个活跃的用户和开发社区。因为当你遇到问题时,你需要答案。当您投资(通过花时间获得使用该工具的技能)时,您希望避免押注于死马。

TL;博士:

  • 要么务实,只测试“好的案例”;希望没有人“愚蠢”到将 try-with-resources 变成老式的 try/catch
  • 更改为不同的模拟框架(例如 mockito,SO 上标记的问题比 jmock 多 20 倍)

(不要误会我的意思:jmock 可能是一个“优秀”框架 - 但重要的是活力。不移动(或移动太慢)的东西已经死了。你不会把钱投资在技术上)

鉴于 jmock 是一个已建立的框架,只需考虑“通过进化实现进步”。含义:获得添加另一个模拟框架的批准;并开始将其用于任何新的事情。这就是我们从 EasyMock 迁移到 Mockito 的方式;这效果非常好。

关于java - 使用 try-with-resource 时调用断言 close,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45185174/

相关文章:

python - pytest-mock assert_called_with 类函数失败

java - spring通用json响应

java - 通过注释而不是 XML 配置 Spring LdapTemplate 的最佳实践?

ios - Xcode 中的单元测试给出 Shell 脚本调用警告

c - 使用 posix 线程的可测试 C 应用程序

c# - (Random.NextDouble() < 1) 的用途是什么

.net - 如何使用 Moq 模拟具有两个索引的索引属性

java - Hibernate:运行查询以获取日期范围内的结果时没有结果

java - BeanFieldGroup 与组合框?

类别的Android单元测试