qileilove

          blog已經(jīng)轉(zhuǎn)移至github,大家請訪問 http://qaseven.github.io/

          DAO test

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


            通過定義的靜態(tài)字段可以獲取一組代表一個數(shù)據(jù)操作的子類對象,比如DatabaseOperation .INSERT,返回 InsertOperation,通過執(zhí)行execute方法把數(shù)據(jù)集插入到數(shù)據(jù)庫。例如:
            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ù)庫,初始化測試數(shù)據(jù)。
            Assertion
            唯一的方法,assertEqual,斷言兩個數(shù)據(jù)集或數(shù)據(jù)表相同。
            更多關(guān)于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中包含的表中的數(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);
          }
          /**
          * 驗證file中包含的表中的數(shù)據(jù)和數(shù)據(jù)庫中的相應(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);
          }
          /**
          * 驗證指定的表為空
          *
          * @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) 評論(0)  編輯  收藏 所屬分類: 數(shù)據(jù)庫

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

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 安陆市| 庆安县| 永寿县| 厦门市| 东城区| 田东县| 洞头县| 福海县| 分宜县| 阜宁县| 台江县| 井陉县| 彭泽县| 阿鲁科尔沁旗| 文昌市| 安丘市| 德兴市| 绥宁县| 玉溪市| 墨玉县| 水富县| 万州区| 双辽市| 定兴县| 桂阳县| 海门市| 白水县| 蒙城县| 会理县| 绥滨县| 靖远县| 麻城市| 错那县| 浑源县| 莒南县| 贡觉县| 东乡族自治县| 祁门县| 吴川市| 西林县| 金川县|