Mockito:使用mock as参数从构造函数调用获取ClassCastException

时间:2014-09-10 15:43:46

标签: junit testng mockito powermock

我希望单元测试一个签名为

的方法
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的逻辑。 它还具有为此构造函数提供第三个参数的逻辑,但它将其作为Objects

的数组提供

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

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

new ARRAY(arrayDescriptorMock, connectionMock, objectMock)

此构造函数调用在作为测试

的一部分执行时抛出异常

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

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

Attempt1

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

ATTEMPT2

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

图例: arrayDescriptorMock:ArrayDescriptor的模拟

connectionMock:连接模拟

objectArrayMock:模拟代码为

的对象数组的模拟
  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类的模拟对象。为什么不会发生这种情况?为什么要尝试施放?

2 个答案:

答案 0 :(得分:2)

答案的开头是在例外的直接消息中。

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

代码显示来自oracle驱动程序的oracle.sql.ARRAY对象不接受实现JDBC接口的任何Object然后mocks ),例如{ {1}}。这是以任何方式预期的任何连接器架构,包括JDBC,因为它遵循JCA原则。

为什么会出现这种情况是因为来自JCA实现的对象需要在内部了解/与自己的对象进行交互。 java.sql.Connection接口只是客户端必须存在的最小可用合约(此处为JDBC)。

因此,考虑到这一事实并且java.sql.Connection是一个oracle驱动程序类型,那么预期此对象需要内部oracle.sql.ARRAY才能正常运行。嘲an oracle.sql.Connection的胜利甚至是好的oracle.jdbc.OracleConnection可能会以超出预期的方式使用此类型,并最终导致地狱般的固定。

我们,模仿者,说:"不要嘲笑你不拥有的类型。这是模仿ARRAY的。但在这个测试中,我不明白为什么有人会测试由其他人编写的代码,特别是JDBC驱动程序,但如果其中一个实际上是驱动程序开发人员。

如果您需要测试DAO或存储库使用oracle.sql.Connection,那么编写ARRAY(使用真正的Oracle)会更合适。


为了回答这个意图,我想说肯定会去找IT。这就是原因:

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

  • 也许oracle驱动程序中有一些可以提供帮助的隐藏类型,但这只是一个可能,如果以后团队决定更改数据库,那么会引入脆弱性和重构难度。< / LI>
  • 或者您可以使用Integration Tests的模拟,但这会破坏此特定测试的目的

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

答案 1 :(得分:2)

oracle.sql.ARRAY需要oracle.jdbc.OracleConnection查看ARRAY类的代码:

  

如果(!(paramArrayDescriptor.getInternalConnection()。isDescriptorSharable(((oracle.jdbc.OracleConnection)paramConnection).physicalConnectionWithin()))){     抛出新的SQLException(&#34;无法构造ARRAY实例,无效   连接&#34); }

因此,当您尝试提供由PowerMockito模拟的java.sql.Connection时,无法在oracle.jdbc.OracleConnection中投放。

所以使用这个 -

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