使用try-with-resource

时间:2017-07-19 08:36:18

标签: 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块中引发的。

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

有没有办法用jMock实现这个目标?

不,我不是要求对图书馆提出建议。所以请不要标记为偏离主题。

2 个答案:

答案 0 :(得分:2)

关键点在于:您不必验证try-with-resources是否正常工作,因为Java语言规范认为它应该可行。如果您找不到与jMock一起使用的问题的解决方案 - 那么请选择"接下来最好的事情"。那将是:为"好路径编写测试用例"确保连接关闭。

意思是:你的问题是这个测试是抛出异常,这意味着关闭调用是隐藏的"来自你但是当你编写一个测试,其中抛出了没有异常时,你应该能够验证是否调用了close()

当您使用try-with-resources时,您可以推断出它也将被调用为坏路径。当然,这不是很优雅。但这是一个务实的解决方案 - 根植于你正在使用"模糊"嘲弄框架!

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

这在某种程度上是自以为是,但jmock是"不合理"用于生产用途 - 仅仅因为"没有人"正在使用它。它似乎是一个几乎死了#34;项目。当你转向jmock site时,你很容易碰到"死了"链接。当你转向他们github的存在时 - 你会发现自2016年以来只有少数提交。无论如何只需要几个提交者,并且提交的数量在很长一段时间内非常接近于零。

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

TL; DR:

  • 要么是务实的,只要测试好的情况&#34 ;;希望没有人是"愚蠢的"足以将try-with-resources转入oldschool try / catch
  • 更改为另一个模拟框架(例如mockito,其中有20个标记在SO上的问题多于jmock)

(不要误会我的意思:jmock可能是一个很好的框架 - 但重要的是活力。不会移动的东西(或移动太慢了。你不会在技术上投入资金)

鉴于jmock是一个既定的框架 - 只需考虑"进化的进步"。含义:获得批准添加另一个模拟框架;并开始使用它来做任何 new 。这就是我们从EasyMock转移到Mockito的方式;这非常有效。

答案 1 :(得分:0)

所以我在使用Jmock和带有CloseableHttpResponse和CloseableHttpClient的try-with-resources时遇到了类似的问题。这是由于Jmock的内部期望该方法(在这种情况下为“关闭”)只能通过源代码调用(请参见What on earth is "Self-suppression not permitted" and why is Javac generating code which results in this error?)。

您需要在使用的每个可关闭对象上模拟close方法。即在Connection和PreparedStatement上。