通過(guò)H2+Dbunit+JUnit進(jìn)行數(shù)據(jù)庫(kù)相關(guān)的測(cè)試
很多時(shí)候我們?cè)诰帉?xiě)測(cè)試時(shí),常常需測(cè)試一些需要訪問(wèn)數(shù)據(jù)庫(kù)的功能,在處理這樣的測(cè)試時(shí),為了保證測(cè)試可模擬測(cè)試場(chǎng)景、可重復(fù)執(zhí)行,我們需要一個(gè)簡(jiǎn)單容易的數(shù)據(jù)準(zhǔn)備工具;一個(gè)隔離的干凈的數(shù)據(jù)庫(kù)環(huán)境。
為了達(dá)到以上目標(biāo),可以通過(guò)許多方式達(dá)到,比如同事愛(ài)用的用各種Model的Builder+數(shù)據(jù)庫(kù)事務(wù)回滾(跨庫(kù)問(wèn)題),和有些人愛(ài)用的每次執(zhí)行前都清掉數(shù)據(jù)庫(kù)然后Dbunit(如果沒(méi)有自己獨(dú)立的庫(kù)往往影響其他同事工作)。
最后自己選擇的方法是結(jié)合builder(準(zhǔn)備待插入數(shù)據(jù))和Dbunit,然后使用H2數(shù)據(jù)庫(kù)并開(kāi)啟內(nèi)存模式和MySQL兼容模式來(lái)處理與數(shù)據(jù)庫(kù)相關(guān)的測(cè)試。(是的,學(xué)習(xí)了Spring-Side的測(cè)試方法(*^__^*) ),這樣每次測(cè)試時(shí)都可以有個(gè)相當(dāng)干凈的環(huán)境,并且不需要為了跑個(gè)測(cè)試單獨(dú)建庫(kù)建表。
對(duì)于Dbunit、H2的具體情況請(qǐng)查看相關(guān)文檔,這里就不做細(xì)述,下面就來(lái)看下如何結(jié)合H2\Dbunit進(jìn)行測(cè)試。
首先,我們需要準(zhǔn)備H2數(shù)據(jù)庫(kù)。既然H2是“內(nèi)嵌式”數(shù)據(jù)庫(kù),自然不需傳統(tǒng)數(shù)據(jù)庫(kù)“安裝”過(guò)程,如果您使用maven,要做的僅僅是加入依賴包
<dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.3.160</version> <scope>test</scope> </dependency>
就可以在代碼中直接連接h2并使用了。
接下來(lái)要做的是并獲取他的數(shù)據(jù)源以進(jìn)行接下來(lái)的操作。
這里根據(jù)項(xiàng)目使用的不通技術(shù)可以有不同選擇,比如項(xiàng)目使用了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就被你所擁有…但這個(gè)在使用過(guò)程中發(fā)現(xiàn)個(gè)問(wèn)題就是通過(guò)使用embedded-database標(biāo)簽沒(méi)法自己定義連接URL開(kāi)啟MySQL模式,且因?yàn)轫?xiàng)目使用mybatis沒(méi)法像Hibernate之類的ORM容易處理sql語(yǔ)法不匹配問(wèn)題,最后沒(méi)有選擇使用。
如需要通過(guò)使用spring管理配置數(shù)據(jù)源,可以考慮自己實(shí)現(xiàn)DataSourceFactoryBean,可以配置URL、用戶名、密碼和建表腳本路徑,然后在bean初始化時(shí)通過(guò)配置的建表語(yǔ)句建表…
接下來(lái)要說(shuō)的是沒(méi)有使用spring的情況,這時(shí)使用老辦法,直接連接數(shù)據(jù)庫(kù)。
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來(lái)簡(jiǎn)化建表語(yǔ)句的執(zhí)行,至于DataSource類型的選擇,可以根據(jù)情況自己更換。
然后可以創(chuàng)建一個(gè)Test的基類讓測(cè)試代碼方便使用dataSource
public abstract class AbstractDBAwareTestCase { protected QueryRunner queryRunner ; protected DataSource dataSource;
protected void setUp() throws Exception { dataSource = getDataSource(); initSchema(dataSource); queryRunner = new QueryRunner(dataSource); } }
之后寫(xiě)的單元測(cè)試只需繼承此類就可以獲取已建好庫(kù)的數(shù)據(jù)進(jìn)行測(cè)試。
在得到數(shù)據(jù)源后,我們要做的操作是用Dbunit向這個(gè)嶄新的數(shù)據(jù)庫(kù)里頭灌入數(shù)據(jù)。
覺(jué)得Dbunit還是不夠好用?這里推薦直接copy更改spring-side的Fixtures類
(http://springside.googlecode.com/svn/springside4/trunk/modules/core/src/main/java/org/springside/modules/test/data/Fixtures.java)
這個(gè)類有個(gè)問(wèn)題是需要對(duì)spring的resource相關(guān)類有依賴(獲取classpath下的文件之類的),如果有問(wèn)題還需更改下。
最后看個(gè)簡(jiǎn)單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模式,但有的語(yǔ)句還是不兼容,比如建表語(yǔ)句中的comment\innodb\tinyint可能都需要稍作調(diào)整。
posted on 2011-11-12 01:16 lysu 閱讀(4910) 評(píng)論(1) 編輯 收藏