본문 바로가기

개발환경

Spring-3.1 + Oracle10g 에서 dbUnit 테스트하기

기본환경:

OS: Window 7 SP1

JVM: Java SE 6 (JDK 1.6.0_31, 64bit)

Maven: 3.0.4

Eclipse: JEE-Indigo-SR2


현상:

Spring 3.1.1.RELEASE, JPA 2.0, Hibernate 3.6.10.Final 로 프로젝트를 진행하고 있었다. 처음에 MySql 5.5로 개발을 진행하다가 Oracle10g로 변경을 했는데 잔잔한 몇가지 경우를 빼고는 별탈없이 단위테스트케이스를 만들면서 개발이 가능했다. 그런데 

@Lob 을 테스트하면서 다음과 같은 예외가 발생했다.

java.lang.ClassCastException: java.lang.String cannot be cast to oracle.sql.CLOB

at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:9229) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]

at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8843) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]

at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:9316) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]

at org.dbunit.dataset.datatype.ClobDataType.setSqlValue(ClobDataType.java:71) ~[dbunit-2.4.9.jar:na]

at org.dbunit.database.statement.SimplePreparedStatement.addValue(SimplePreparedStatement.java:73) ~[dbunit-2.4.9.jar:na]

at org.dbunit.database.statement.AutomaticPreparedBatchStatement.addValue(AutomaticPreparedBatchStatement.java:63) ~[dbunit-2.4.9.jar:na]

at org.dbunit.operation.AbstractBatchOperation.execute(AbstractBatchOperation.java:189) ~[dbunit-2.4.9.jar:na]

at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79) ~[dbunit-2.4.9.jar:na]

at com.github.springtestdbunit.DbUnitRunner.setupOrTeardown(DbUnitRunner.java:159) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.DbUnitRunner.beforeTestMethod(DbUnitRunner.java:70) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.DbUnitTestExecutionListener.beforeTestMethod(DbUnitTestExecutionListener.java:136) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.TestExecutionListenerChain$3.call(TestExecutionListenerChain.java:92) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.TestExecutionListenerChain.runChain(TestExecutionListenerChain.java:125) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.TestExecutionListenerChain.forwards(TestExecutionListenerChain.java:114) ~[spring-test-dbunit-1.0.0.jar:na]

at com.github.springtestdbunit.TestExecutionListenerChain.beforeTestMethod(TestExecutionListenerChain.java:90) ~[spring-test-dbunit-1.0.0.jar:na]

at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:358) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) [junit-4.10.jar:na]

at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) [junit-4.10.jar:na]

at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) [junit-4.10.jar:na]

at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) [junit-4.10.jar:na]

at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) [junit-4.10.jar:na]

at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) [junit-4.10.jar:na]

at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.junit.runners.ParentRunner.run(ParentRunner.java:300) [junit-4.10.jar:na]

at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]

at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na]

at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]

at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]


이 예외의 원인은 

java.lang.ClassCastException: java.lang.String cannot be cast to oracle.sql.CLOB

에서 유추할 수 있으나 왜 그런지는 알수가 없다. 예외스택을 좀더 탐색해 보면 


oracle.jdbc.driver.OraclePreparedStatement -> org.dbunit.database.statement.SimplePreparedStatement. -> com.github.springtestdbunit.DbUnitRunner.setupOrTeardown


등으로 역추적할 수 있는데 dbUnit을 Annotation 기반으로 테스트 환경을 구축하기 위해서

pom.xml

<dependencies>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.10</version>

</dependency>

<dependency>

<groupId>org.dbunit</groupId>

<artifactId>dbunit</artifactId>

<version>2.4.9</version>

<type>jar</type>

</dependency>

<dependency>

<groupId>org.springframework</groupId>

<artifactId>spring-test</artifactId>

<version>${spring.version}</version>

</dependency>

<dependency>

<groupId>com.github.springtestdbunit</groupId>

<artifactId>spring-test-dbunit</artifactId>

<version>1.0.0</version>

</dependency>

</dependencies>


DbUnitTestTemplate .java

import org.springframework.test.context.ContextConfiguration;

import org.springframework.test.context.TestExecutionListeners;

import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;

import org.springframework.test.context.support.DirtiesContextTestExecutionListener;

import org.springframework.test.context.transaction.TransactionConfiguration;


import com.github.springtestdbunit.DbUnitTestExecutionListener;

import com.github.springtestdbunit.TransactionDbUnitTestExecutionListener;

import com.github.springtestdbunit.annotation.DbUnitConfiguration;


/**

 * DbUnit에대한 테스트설정을 공유하기 위한 추상클래스

 * @author <a href="mailto:byleem@nextree.co.kr">임병인</a>

 * @since 2012. 11. 15.

 */

@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })

// @DbUnitConfiguration(databaseConnection = "test_ASN_DS")

@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")

@TestExecutionListeners({

  DependencyInjectionTestExecutionListener.class,

  DirtiesContextTestExecutionListener.class,

  TransactionDbUnitTestExecutionListener.class, // needed if using transactions otherwise use TransactionalTestExecutionListener.class

  DbUnitTestExecutionListener.class })

@TransactionConfiguration(transactionManager = "testTransactionManager", defaultRollback = true)

public abstract class DbUnitTestTemplate {


}


applicationContext-test.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:tx="http://www.springframework.org/schema/tx"

xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"

xsi:schemaLocation="

http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd

http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">

<context:component-scan base-package="com.acelife.asn" />

<context:property-placeholder location="classpath:application-test.properties"/>

<bean id="test_ASN_DS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">

<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />

<property name="url" value="jdbc:oracle:thin:@db.ace.dev:1522:xe" />

<property name="username" value="........" />

<property name="password" value="........" />

</bean>

<bean id="testEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

<property name="dataSource" ref="test_ASN_DS" />

<property name="persistenceUnitName" value="pun_asn" />

<property name="persistenceXmlLocation" value="classpath:META-INF/persistence-test.xml" />

<property name="loadTimeWeaver">

<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />

</property>

</bean>

<bean id="testTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

<property name="entityManagerFactory" ref="testEMF" />

</bean>

<tx:annotation-driven transaction-manager="testTransactionManager" />

<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />

<ehcache:annotation-driven cache-manager="ehCacheManager" />


<!-- dbUnit -->

<bean id="dbUnitDatabaseConnection" class="com.github.springtestdbunit.bean.DatabaseDataSourceConnectionFactoryBean">

<property name="dataSource" ref="test_ASN_DS" />

<property name="databaseConfig">

<bean class="com.github.springtestdbunit.bean.DatabaseConfigBean">

<property name="skipOracleRecyclebinTables" value="true" />

<property name="datatypeFactory">

<bean class="org.dbunit.ext.oracle.Oracle10DataTypeFactory" />

</property>

</bean>

</property>

</bean>

</beans>


와 같이 설정했다.


지면관계상 설정을 해결안까지 같이 설정해 놨는데 위의 오라클 Lob문제는 

http://old.nabble.com/DBUNIT%3A-exception-when-using-xmlFlatFile-to-load-to-a-table-with-a-clob-td28986386.html

를 참고하여 


<property name="datatypeFactory">

<bean class="org.dbunit.ext.oracle.Oracle10DataTypeFactory" />

</property>


와 같이 datatypeFactory를 설정해주는 것이었다. 그리고 

@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")

와 같이 설정해줌으로써 DataSource 대신 IDatabaseConnection을 DBUnit에 전달해 주는 방법이다.


아마 대다수의 DB에서는 이런 문제가 발생하지 않는다고 하는데... 그건 알수가 없는 노릇이고... 어쨌든 바쁜와중에도 이문제는 꼭 포스팅해야한다는 생각이 들었다. 나를 위해서... ^^;