通過H2+Dbunit+JUnit進行數據庫相關的測試
很多時候我們在編寫測試時,常常需測試一些需要訪問數據庫的功能,在處理這樣的測試時,為了保證測試可模擬測試場景、可重復執行,我們需要一個簡單容易的數據準備工具;一個隔離的干凈的數據庫環境。
為了達到以上目標,可以通過許多方式達到,比如同事愛用的用各種Model的Builder+數據庫事務回滾(跨庫問題),和有些人愛用的每次執行前都清掉數據庫然后Dbunit(如果沒有自己獨立的庫往往影響其他同事工作)。
最后自己選擇的方法是結合builder(準備待插入數據)和Dbunit,然后使用H2數據庫并開啟內存模式和MySQL兼容模式來處理與數據庫相關的測試。(是的,學習了Spring-Side的測試方法(*^__^*) ),這樣每次測試時都可以有個相當干凈的環境,并且不需要為了跑個測試單獨建庫建表。
對于Dbunit、H2的具體情況請查看相關文檔,這里就不做細述,下面就來看下如何結合H2\Dbunit進行測試。
首先,我們需要準備H2數據庫。既然H2是“內嵌式”數據庫,自然不需傳統數據庫“安裝”過程,如果您使用maven,要做的僅僅是加入依賴包
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.160</version> <scope>test</scope> </dependency>
就可以在代碼中直接連接h2并使用了。
接下來要做的是并獲取他的數據源以進行接下來的操作。
這里根據項目使用的不通技術可以有不同選擇,比如項目使用了spring,那可以使用Spring Embedded database support(http://static.springsource.org/spring/docs/3.0.0.M4/spring-framework-reference/html/ch12s08.html)
<jdbc:embedded-database id="dataSource"> <jdbc:script location="classpath:schema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:embedded-database>
然后dataSource就被你所擁有…但這個在使用過程中發現個問題就是通過使用embedded-database標簽沒法自己定義連接URL開啟MySQL模式,且因為項目使用mybatis沒法像Hibernate之類的ORM容易處理sql語法不匹配問題,最后沒有選擇使用。
如需要通過使用spring管理配置數據源,可以考慮自己實現DataSourceFactoryBean,可以配置URL、用戶名、密碼和建表腳本路徑,然后在bean初始化時通過配置的建表語句建表…
接下來要說的是沒有使用spring的情況,這時使用老辦法,直接連接數據庫。
public final class H2DatabaseUtil { private static final Logger logger = LoggerFactory.getLogger(H2DatabaseUtil.class);
private H2DatabaseUtil() { }
public static DataSource getDataSource() { if (logger.isDebugEnabled()) { logger.debug("get H2 data source."); } SimpleDriverDataSource dataSource = new SimpleDriverDataSource(); dataSource.setDriverClass(org.h2.Driver.class); dataSource.setUsername("sa"); dataSource.setPassword(""); dataSource.setUrl("jdbc:h2:mem:testdb;MODE=MYSQL;DB_CLOSE_DELAY=-1"); return dataSource; }
public static void initSchema(DataSource ds) { if (logger.isDebugEnabled()) { logger.debug("start init schema."); } try { QueryRunner queryRunner = new QueryRunner(ds); queryRunner.update("drop all objects"); if (logger.isDebugEnabled()) { logger.debug("delete all object k.o."); } try { queryRunner.update("runscript from '" + new DefaultResourceLoader() .getResource("schema.sql").getURL().toString() + "'"); if (logger.isDebugEnabled()) { logger.debug("rebuild the schema finish! f~resh meat..."); } } catch (IOException e) { logger.error(e.getMessage(), e); } } catch (SQLException e) { logger.error(e.getMessage(),e); } } }
這里使用了DbUtils來簡化建表語句的執行,至于DataSource類型的選擇,可以根據情況自己更換。
然后可以創建一個Test的基類讓測試代碼方便使用dataSource
public abstract class AbstractDBAwareTestCase { protected QueryRunner queryRunner ; protected DataSource dataSource;
protected void setUp() throws Exception { dataSource = getDataSource(); initSchema(dataSource); queryRunner = new QueryRunner(dataSource); } }
之后寫的單元測試只需繼承此類就可以獲取已建好庫的數據進行測試。
在得到數據源后,我們要做的操作是用Dbunit向這個嶄新的數據庫里頭灌入數據。
覺得Dbunit還是不夠好用?這里推薦直接copy更改spring-side的Fixtures類
(http://springside.googlecode.com/svn/springside4/trunk/modules/core/src/main/java/org/springside/modules/test/data/Fixtures.java)
這個類有個問題是需要對spring的resource相關類有依賴(獲取classpath下的文件之類的),如果有問題還需更改下。
最后看個簡單Test的使用列子:
public class AgentRepositoryTest extends AbstractDBAwareTestCase { private IAgentRepository agentRepository;
@Before public void setUp() throws Exception { super.setUp(); reloadAllTable(dataSource, "/data/agent-data.xml"); agentRepository = Lookup.getDefault().lookup(IAgentRepository.class); }
@Test public void should_find_agent_by_id() throws Exception { Agent agent = agentRepository.findById(dataSource, 1); assertNotNull(agent); assertThat(agent.getSimpleName(), is("demoa")); } }
ps:還需要注意的是雖然使用H2的MySQL模式,但有的語句還是不兼容,比如建表語句中的comment\innodb\tinyint可能都需要稍作調整。