【问题标题】:Mockito: Getting ClassCastException from a constructor call with a mock as argumentMockito:从使用模拟作为参数的构造函数调用中获取 ClassCastException
【发布时间】:2014-09-10 15:43:46
【问题描述】:

我想单元测试一个带有签名的方法

public oracle.sql.ARRAY  methodA(java.sql.Connection connection)

连接对象被模拟使用

Mockito.mock(Connection.class)

并在测试时传递给methodA。

oracle.sql.ARRAY 有一个构造函数

new ARRAY(oracle.sql.ArrayDescriptor, Connection, Object)

methodA 具有获取 ArrayDescriptor 的逻辑。 它还具有为此构造函数提供第三个参数的逻辑,但它以对象数组的形式提供它

我正在模拟这两组逻辑并传递一个模拟的 ArrayDescriptor 对象和一个模拟的 Object 数组。 所有这些模拟都能正常工作。

所以我的构造函数调用就像

new ARRAY(arrayDescriptorMock, connectionMock, objectMock)

此构造函数调用在作为测试的一部分执行时会引发异常

java.lang.ClassCastException:$java.sql.Connection$$EnhancerByMockitoWithCGLIB$$2427854e 无法转换为 oracle.jdbc.OracleConnection

为了避免这个错误,我做了很多尝试来模拟这个构造函数调用。然而,每一个都被证明是徒劳的。我正在为构造函数记录我的模拟代码

尝试1

 PowerMockito.whenNew(ARRAY.class).withParameterTypes(ArrayDescriptor.class, Connection.class, Object.class)
            .withArguments(any(ArrayDescriptor.class), any(Connection.class), any(Object[].class))
            .thenReturn(expectedArray);

尝试2

  PowerMockito.whenNew(ARRAY.class).withArguments(arrayDescriptorMock,connectionMock,objectArrayMock).thenReturn(expectedArray);

传说: arrayDescriptorMock: ArrayDescriptor 的模拟

connectionMock:连接模拟

objectArrayMock:对象数组的mock,模拟代码为

  Object[] structArrayMock = new Object[2];
       STRUCT obj1 = mock(STRUCT.class);
       STRUCT obj2 = mock(STRUCT.class);
       structArrayMock[0] = obj1;
       structArrayMock[1] = obj2;

注意事项: 被测方法有以下我没有模拟的代码

 Object[] objArray = new Object[2];

我正在使用 PowerMockito、Mockito 和 TestNG。但是,我认为这个问题与TestNG无关,因此JUNIT标签应该没问题。

请告知为什么会出现这个 ClassCastException 以及如何避免它?

编辑 我也相信 Mockito 应该拦截构造函数调用。这意味着它不应该允许执行真正的构造函数。假设简单地返回 ARRAY 类的模拟对象。为什么这不会发生?为什么要尝试 Casting ?

【问题讨论】:

    标签: junit testng mockito powermock


    【解决方案1】:

    答案的开头是异常的直接消息。

    ... cannot be cast to oracle.jdbc.OracleConnection
    

    代码显示 oracle.sql.ARRAY 来自 oracle 驱动程序的对象只是不接受任何实现 JDBC 接口的 Object然后是模拟),例如 java.sql.Connection。这在任何连接器架构中都是可以预料到的,包括 JDBC,因为它遵循 JCA 原则。

    为什么这是预期的,因为来自 JCA 实现的对象需要在内部知道/与它们自己的对象交互。 java.sql.Connection 接口只是此 SPI(那里的 JDBC)客户端必须存在的最小可用合约。

    因此,鉴于这一事实以及 oracle.sql.ARRAY 是一个 oracle 驱动程序类型,那么预计该对象需要一个内部 oracle.sql.Connection 才能正常运行。模拟一个 oracle.jdbc.OracleConnection 甚至都不好,因为 ARRAY 可能以比预期更多的方式使用这种类型,并且最终会导致地狱般的固定。

    通常我们,mockists,会说:“不要模拟你不拥有的类型。那是为了模拟oracle.sql.Connection . 但是在这个测试中,我不明白为什么有人会测试由其他人编写的代码,尤其是 JDBC 驱动程序,但如果一个人实际上是驱动程序开发人员。

    如果您需要测试 DAO 或存储库是否使用 ARRAY,那么编写 Integration Tests(使用真正的 Oracle)会更合适。


    要回答这个意图,我会说肯定会选择 IT。原因如下:

    实际上,由于测试代码在某些时候涉及专有类型。这些类型带有一个驱动程序,该驱动程序必须连接到一个真实的数据库以提供一个可以使用的连接。我没有看到一种简单的单元测试方法,

    • 也许 oracle 驱动程序中有一些隐藏类型可以提供帮助,但这只是一种可能,如果以后团队决定更改数据库,这会带来脆弱性和重构困难。
    • 或者您可以使用 ARRAY 的模拟,但这会破坏此特定测试的目的

    我必须补充一点,第一个原因是当我的系统需要连接到另一个系统时,我总是编写集成测试。 IT 有助于覆盖应用程序边界,包括持久性。我通常调用存储库的业务 API(它们是具有面向业务的 API 的 DAO),他们使用任何数据存储库来完成他们的工作。 Oracle 或其他人可以更改驱动程序的实现、删除类型等。我不必重写这些测试,只需实际实现即可。

    【讨论】:

    • 感谢您的回答。我从中学到了很多。这条评论真的很关注最后一句话。我有一个将用户定义类型 (UDT) 转换为 ARRAY 实例的方法。我需要对这个功能进行单元测试。我应该如何对此进行单元测试,或者我应该放弃对这个进行单元测试的想法?
    【解决方案2】:

    oracle.sql.ARRAY 需要 oracle.jdbc.OracleConnection 参见 ARRAY 类中的代码:

    if(!(paramArrayDescriptor.getInternalConnection().isDescriptorSharable(((oracle.jdbc.OracleConnection)paramConnection).physicalConnectionWithin()))){ throw new SQLException("不能构造 ARRAY 实例,无效 连接"); }

    因此,当您尝试提供由 PowerMockito 模拟的 java.sql.Connection 时,不能将其转换为 oracle.jdbc.OracleConnection

    所以使用这个 -

      Connection conn = PowerMockito.mock(OracleConnection.class);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-12
      相关资源
      最近更新 更多