qileilove

blog已经转移至github,大家请访问 http://qaseven.github.io/

DAO test

至此,一个基于MVC的基本Android应用程序已经初步形成了。
  下面我们来实现一个具有TabHost的布局的典型Android应用,由于我们基本上可以不考虑Android 4.x以前的版本,因此我对TabHost布局的实现将采用Fragment来实现,而不是采用旧的ActivityGroup来实现。
  同时,我们希望我们的应用程序可以适用于不同的项目,因此需要TabHost上的图片及文字可以非常方便的进行更换。我们采用下部有5个选项的布局,其中中间的选项可以突出显示,选中某个选项,目前仅显示对应Fragmentation的名字。
  好了,需求基本说清楚了,下面我们就开始一步步实现吧。
  首先是基于Fragment的TabHost布局实现,原理很简单,在MainActivity的布局文件里添加如下代码即可:
<?xml version="1.0" encoding="utf-8"?>
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- 实现Tab标签的居底主要是通过设置属性 android:layout_weight="1" -->
<!-- 还要注意FrameLayout标签的位置,要写在TabWidget标签的前面 -->
<FrameLayout
android:id="@android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_gravity="center_horizontal"
android:layout_weight="1">
<fragment
android:id="@+id/j_dynamicFragment"
android:name="com.bjcic.wkj.gui.DynamicFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="@+id/j_findFragment"
android:name="com.bjcic.wkj.gui.FindFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="@+id/j_shareFragment"
android:name="com.bjcic.wkj.gui.ShareFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="@+id/j_snsFragment"
android:name="com.bjcic.wkj.gui.SnsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="@+id/j_moreFragment"
android:name="com.bjcic.wkj.gui.MoreFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<TabWidget
android:id="@android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="60dip"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="-2dp"
android:layout_marginRight="-2dp"
android:background="@null" />
</LinearLayout>
</TabHost>
好的单元测试应该是原子性的,独立的,不应依赖其他测试和上下文,但是要测试数据读写是否正确,就必须涉及初始数据的加载,数据修改的还原等操作。对于初始数据的加载,手动输入很麻烦,一个解决方案就是使用Dbunit,从Xml文件甚至Excel中加载初始数据到数据库,是数据库的值达到一个已知状态。同时还可以使用Dbunit,对数据库的结果状态进行判断,保证和期望的一致。数据修改的还原,可以依赖Spring TransactionalTests,在测试完成后回滚数据库。
  Dbunit还可以对数据的现有数据进行备份,还原,清空现有数据,一个好的测试实践是每一个开发人员一个测试数据库,进而对数据库的数据状态有更好的控制,但现实可能会是共享同一个测试库,所以这种情况下,测试的编写必须多做一些考虑。
  待测试的类:
package com.test.dbunit.dao.impl;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
import com.test.dbunit.dao.UserDao;
import com.test.dbunit.entity.User;
public class DefaultUserDao extends BaseDao implements UserDao {
private static String QUERY_BY_NICK = "select * from user where user.nick = ?";
private static String REMOVE_USER = "delete from user where user.nick = ?";
private static String INSERT_USER = "insert into user(nick,password) values(?, ?)";
private static String UPDATE_USER = "update user set user.password = ? where user.nick = ?";
@Override
public User getUserByNick(String nick) {
return (User) getJdbcTemplate().queryForObject(QUERY_BY_NICK,new Object[]{nick}, new RowMapper(){
@Override
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();
user.setNick(rs.getString("nick"));
user.setPassword(rs.getString("password"));
return user;
}
});
}
@Override
public void remove(String nick) {
getJdbcTemplate().update(REMOVE_USER, new Object[]{nick});
}
@Override
public void save(User user) {
getJdbcTemplate().update(INSERT_USER, new Object[]{user.getNick(), user.getPassword()});
}
@Override
public void update(User user) {
getJdbcTemplate().update(UPDATE_USER, new Object[]{user.getPassword(), user.getNick()});
}
}
  单元测试:
  需要注意的地方就是,DataSourceUtils.getConnection(datasource) , 通过这种方式获得数据库连接初始化Dbunit,能够保证Dbunit使用的数据连接和当前事务的数据库连接相同,保证能够在参与到事务中。Spring的TransactionManager会在开始事务时把当前连接保存到ThreadLocal中,DataSourceUtils.getConnection方法,首先从ThreadLocal中获取连接。
  user001.xml
  Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
<user nick="user001" password="password001" />
</dataset>
  使用dbunit,可以通过xml文件定义数据集,也可以使用其他方式定义,比如Excel,编程方式。
  Dbunit的主要构件
  IDatabaseConnection
  数据库链接。实现类有DatabaseConnection 和DatabaseDataSourceConnection ,执行数据库操作时需要一个连接。
  IDataSet
  数据集,数据集可以从Xml文件Excel等外部文件获取,也可以从数据库查询,或者编程方式构件,数据集可以作为初始数据插入到数据库,也可以作为断言的依据。另外还有IDatatable等辅助类。
  比如在updateUser测试中,使用了QueryDataSet,从数据库中构建一个Dataset,再通过FlatXmlDataSet从Xml文件中构建一个Dataset,断言这两个Dataset相同。
QueryDataSet actual = new QueryDataSet(conn);
actual.addTable("user", "select * from user where user.nick = 'user001'");
IDataSet expected = new FlatXmlDataSet(new ClassPathResource(
"com/taobao/dbunit/dao/user001_updated.xml").getFile());
Assertion.assertEquals(expected, actual);
DatabaseOperation


  通过定义的静态字段可以获取一组代表一个数据操作的子类对象,比如DatabaseOperation .INSERT,返回 InsertOperation,通过执行execute方法把数据集插入到数据库。例如:
  IDataSet origen = new FlatXmlDataSet(new ClassPathResource(
  "com/taobao/dbunit/dao/user001.xml").getFile());
  DatabaseOperation.INSERT.execute(conn, origen);
  从Xml文件中构建DataSet,使用Insert插入到数据库,初始化测试数据。
  Assertion
  唯一的方法,assertEqual,断言两个数据集或数据表相同。
  更多关于Dbunit的组件的介绍:http://www.dbunit.org/components.html
  PS:使用Oracle的时候,初始化DatabaseConnection需要传入scheme。new DatabaseConnection(conn,SCHEMA_NAME ) ,SCHMEA_NAME需要大写。
  附件提供所有代码下载
package com.taobao.dbunit.dao;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.dbunit.Assertion;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.DefaultDataSet;
import org.dbunit.dataset.DefaultTable;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Assert;
import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;
@ContextConfiguration(locations = { "classpath:testApplicationContext.xml" })
@TransactionConfiguration(defaultRollback = true)
public class BaseDaoTest extends AbstractTransactionalJUnit4SpringContextTests {
@Autowired
private DataSource dataSource;
private IDatabaseConnection conn;
@Before
public void initDbunit() throws Exception {
conn = new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
}
/**
* 清空file中包含的表中的数据,并插入file中指定的数据
*
* @param file
* @throws Exception
*/
protected void setUpDataSet(String file) throws Exception {
IDataSet dataset = new FlatXmlDataSet(new ClassPathResource(file)
.getFile());
DatabaseOperation.CLEAN_INSERT.execute(conn, dataset);
}
/**
* 验证file中包含的表中的数据和数据库中的相应表的数据是否一致
*
* @param file
* @throws Exception
*/
protected void verifyDataSet(String file) throws Exception {
IDataSet expected = new FlatXmlDataSet(new ClassPathResource(file)
.getFile());
IDataSet dataset = conn.createDataSet();
for (String tableName : expected.getTableNames()) {
Assertion.assertEquals(expected.getTable(tableName), dataset
.getTable(tableName));
}
}
/**
* 清空指定的表中的数据
*
* @param tableName
* @throws Exception
*/
protected void clearTable(String tableName) throws Exception {
DefaultDataSet dataset = new DefaultDataSet();
dataset.addTable(new DefaultTable(tableName));
DatabaseOperation.DELETE_ALL.execute(conn, dataset);
}
/**
* 验证指定的表为空
*
* @param tableName
* @throws DataSetException
* @throws SQLException
*/
protected void verifyEmpty(String tableName) throws DataSetException,
SQLException {
Assert.assertEquals(0, conn.createDataSet().getTable(tableName)
.getRowCount());
}
}
  使用:
@Test
public void updateUser() throws Exception {
setUpDataSet("com/taobao/dbunit/dao/user001.xml");
User user = new User();
user.setNick("user001");
user.setPassword("password002");
userDao.update(user);
verifyDataSet("com/taobao/dbunit/dao/user001_updated.xml");
}

posted on 2013-12-23 10:13 顺其自然EVO 阅读(218) 评论(0)  编辑  收藏 所属分类: 数据库


只有注册用户登录后才能发表评论。


网站导航:
 
<2013年12月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

常用链接

留言簿(55)

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜