qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請(qǐng)?jiān)L問(wèn) http://qaseven.github.io/

          DAO test

          至此,一個(gè)基于MVC的基本Android應(yīng)用程序已經(jīng)初步形成了。
            下面我們來(lái)實(shí)現(xiàn)一個(gè)具有TabHost的布局的典型Android應(yīng)用,由于我們基本上可以不考慮Android 4.x以前的版本,因此我對(duì)TabHost布局的實(shí)現(xiàn)將采用Fragment來(lái)實(shí)現(xiàn),而不是采用舊的ActivityGroup來(lái)實(shí)現(xiàn)。
            同時(shí),我們希望我們的應(yīng)用程序可以適用于不同的項(xiàng)目,因此需要TabHost上的圖片及文字可以非常方便的進(jìn)行更換。我們采用下部有5個(gè)選項(xiàng)的布局,其中中間的選項(xiàng)可以突出顯示,選中某個(gè)選項(xiàng),目前僅顯示對(duì)應(yīng)Fragmentation的名字。
            好了,需求基本說(shuō)清楚了,下面我們就開(kāi)始一步步實(shí)現(xiàn)吧。
            首先是基于Fragment的TabHost布局實(shí)現(xiàn),原理很簡(jiǎn)單,在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" >
          <!-- 實(shí)現(xiàn)Tab標(biāo)簽的居底主要是通過(guò)設(shè)置屬性 android:layout_weight="1" -->
          <!-- 還要注意FrameLayout標(biāo)簽的位置,要寫(xiě)在TabWidget標(biāo)簽的前面 -->
          <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>
          好的單元測(cè)試應(yīng)該是原子性的,獨(dú)立的,不應(yīng)依賴(lài)其他測(cè)試和上下文,但是要測(cè)試數(shù)據(jù)讀寫(xiě)是否正確,就必須涉及初始數(shù)據(jù)的加載,數(shù)據(jù)修改的還原等操作。對(duì)于初始數(shù)據(jù)的加載,手動(dòng)輸入很麻煩,一個(gè)解決方案就是使用Dbunit,從Xml文件甚至Excel中加載初始數(shù)據(jù)到數(shù)據(jù)庫(kù),是數(shù)據(jù)庫(kù)的值達(dá)到一個(gè)已知狀態(tài)。同時(shí)還可以使用Dbunit,對(duì)數(shù)據(jù)庫(kù)的結(jié)果狀態(tài)進(jìn)行判斷,保證和期望的一致。數(shù)據(jù)修改的還原,可以依賴(lài)Spring TransactionalTests,在測(cè)試完成后回滾數(shù)據(jù)庫(kù)。
            Dbunit還可以對(duì)數(shù)據(jù)的現(xiàn)有數(shù)據(jù)進(jìn)行備份,還原,清空現(xiàn)有數(shù)據(jù),一個(gè)好的測(cè)試實(shí)踐是每一個(gè)開(kāi)發(fā)人員一個(gè)測(cè)試數(shù)據(jù)庫(kù),進(jìn)而對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)狀態(tài)有更好的控制,但現(xiàn)實(shí)可能會(huì)是共享同一個(gè)測(cè)試庫(kù),所以這種情況下,測(cè)試的編寫(xiě)必須多做一些考慮。
            待測(cè)試的類(lèi):
          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()});
          }
          }
            單元測(cè)試:
            需要注意的地方就是,DataSourceUtils.getConnection(datasource) , 通過(guò)這種方式獲得數(shù)據(jù)庫(kù)連接初始化Dbunit,能夠保證Dbunit使用的數(shù)據(jù)連接和當(dāng)前事務(wù)的數(shù)據(jù)庫(kù)連接相同,保證能夠在參與到事務(wù)中。Spring的TransactionManager會(huì)在開(kāi)始事務(wù)時(shí)把當(dāng)前連接保存到ThreadLocal中,DataSourceUtils.getConnection方法,首先從ThreadLocal中獲取連接。
            user001.xml
            Xml代碼
          <?xml version="1.0" encoding="UTF-8"?>
          <dataset>
          <user nick="user001" password="password001" />
          </dataset>
            使用dbunit,可以通過(guò)xml文件定義數(shù)據(jù)集,也可以使用其他方式定義,比如Excel,編程方式。
            Dbunit的主要構(gòu)件
            IDatabaseConnection
            數(shù)據(jù)庫(kù)鏈接。實(shí)現(xiàn)類(lèi)有DatabaseConnection 和DatabaseDataSourceConnection ,執(zhí)行數(shù)據(jù)庫(kù)操作時(shí)需要一個(gè)連接。
            IDataSet
            數(shù)據(jù)集,數(shù)據(jù)集可以從Xml文件Excel等外部文件獲取,也可以從數(shù)據(jù)庫(kù)查詢(xún),或者編程方式構(gòu)件,數(shù)據(jù)集可以作為初始數(shù)據(jù)插入到數(shù)據(jù)庫(kù),也可以作為斷言的依據(jù)。另外還有IDatatable等輔助類(lèi)。
            比如在updateUser測(cè)試中,使用了QueryDataSet,從數(shù)據(jù)庫(kù)中構(gòu)建一個(gè)Dataset,再通過(guò)FlatXmlDataSet從Xml文件中構(gòu)建一個(gè)Dataset,斷言這兩個(gè)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


            通過(guò)定義的靜態(tài)字段可以獲取一組代表一個(gè)數(shù)據(jù)操作的子類(lèi)對(duì)象,比如DatabaseOperation .INSERT,返回 InsertOperation,通過(guò)執(zhí)行execute方法把數(shù)據(jù)集插入到數(shù)據(jù)庫(kù)。例如:
            IDataSet origen = new FlatXmlDataSet(new ClassPathResource(
            "com/taobao/dbunit/dao/user001.xml").getFile());
            DatabaseOperation.INSERT.execute(conn, origen);
            從Xml文件中構(gòu)建DataSet,使用Insert插入到數(shù)據(jù)庫(kù),初始化測(cè)試數(shù)據(jù)。
            Assertion
            唯一的方法,assertEqual,斷言?xún)蓚€(gè)數(shù)據(jù)集或數(shù)據(jù)表相同。
            更多關(guān)于Dbunit的組件的介紹:http://www.dbunit.org/components.html
            PS:使用Oracle的時(shí)候,初始化DatabaseConnection需要傳入scheme。new DatabaseConnection(conn,SCHEMA_NAME ) ,SCHMEA_NAME需要大寫(xiě)。
            附件提供所有代碼下載
          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));
          }
          /**
          * 清空f(shuō)ile中包含的表中的數(shù)據(jù),并插入file中指定的數(shù)據(jù)
          *
          * @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);
          }
          /**
          * 驗(yàn)證file中包含的表中的數(shù)據(jù)和數(shù)據(jù)庫(kù)中的相應(yīng)表的數(shù)據(jù)是否一致
          *
          * @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));
          }
          }
          /**
          * 清空指定的表中的數(shù)據(jù)
          *
          * @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);
          }
          /**
          * 驗(yàn)證指定的表為空
          *
          * @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 閱讀(229) 評(píng)論(0)  編輯  收藏 所屬分類(lèi): 數(shù)據(jù)庫(kù)

          <2013年12月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導(dǎo)航

          統(tǒng)計(jì)

          常用鏈接

          留言簿(55)

          隨筆分類(lèi)

          隨筆檔案

          文章分類(lèi)

          文章檔案

          搜索

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          主站蜘蛛池模板: 万州区| 琼结县| 济宁市| 瓮安县| 涿州市| 古蔺县| 永康市| 苍梧县| 通化市| 凤阳县| 宁都县| 佛教| 阿合奇县| 寿宁县| 仁寿县| 济南市| 会理县| 锡林郭勒盟| 承德县| 石林| 富蕴县| 南昌市| 昆山市| 宿州市| 威宁| 武宁县| 尼勒克县| 恩施市| 宝清县| 满洲里市| 伊通| 航空| 临安市| 绵竹市| 永宁县| 烟台市| 久治县| 缙云县| 荔波县| 凤冈县| 会理县|