Android单元测试1.docx
- 文档编号:17839869
- 上传时间:2023-08-04
- 格式:DOCX
- 页数:22
- 大小:315.35KB
Android单元测试1.docx
《Android单元测试1.docx》由会员分享,可在线阅读,更多相关《Android单元测试1.docx(22页珍藏版)》请在冰点文库上搜索。
Android单元测试1
Android单元测试-Sqlite、SharedPreference、Assets、文件操作怎么测?
常用的数据储存有:
sqlite、SharedPreference、Assets、文件。
由于这前三种储取数据方式,都必须依赖android环境,因此要进行单元测试,不能仅仅用junit&mockito了,需要另外的单元测试框架。
接下来,笔者介绍如何使用robolectric进行DAO单元测试。
作者:
键盘男来源:
segmentfault|2016-10-2620:
57
收藏
分享
前言
上篇《Android单元测试-几个重要问题》讲解了“何解决Android依赖、隔离Native方法、静态方法、RxJava异步转同步”这几个Presenter单元测试中常见问题。
如果读者你消化得差不多,就接着看本篇吧。
在日常开发中,数据储存是必不可少的。
例如,网络请求到数据,先存本地,下次打开页面,先从本地读取数据显示,再从服务器请求新数据。
既然如此重要,对这块代码进行测试,也成为单元测试的重中之重了。
笔者在学会单元测试前,也像大多数人一样,写好了sql代码,运行app,报错了....检查代码,修改,再运行app....这真是效率太低了。
有了单元测试做武器后,我写DAO代码轻松了不少,不担心出错,效率也高。
常用的数据储存有:
sqlite、SharedPreference、Assets、文件。
由于这前三种储取数据方式,都必须依赖android环境,因此要进行单元测试,不能仅仅用junit&mockito了,需要另外的单元测试框架。
接下来,笔者介绍如何使用robolectric进行DAO单元测试。
缩写解释:
DAO(DataAccessObject)数据访问对象
Robolectric配置
Robolectric官网:
http:
//robolectric.org/
Robolectric配置很简单的。
build.gradle:
1.dependencies {
2. testCompile "org.robolectric:
robolectric:
3.1.2"
3.}
然后在测试用例XXTest加上注解:
1.@RunWith(RobolectricTestRunner.class)
2.@Config(constants = BuildConfig.class)
3.public class XXTest {
4.}
配置代码是写完了。
不过,别以为这样就完了。
Robolectric最麻烦就是下载依赖!
由于我们生活在天朝,下载国外的依赖很慢,笔者即使有了翻墙,效果也一般,可能是https:
//oss.sonatype.org服务器比较慢。
笔者已经下载好了依赖包,读者们可以到下载robolectric3.1.2的依赖包,按照Readme.md说明操作。
Sqlite
DbHelper:
1.public class DbHelper extends SQLiteOpenHelper {
2.
3. private static final int DB_VERSION = 1;
4.
5. public DbHelper(Context context, String dbName) {
6. super(context, dbName, null, DB_VERSION);
7. }
8. ...
9.}
Bean:
1.public class Bean {
2. int id;
3. String name = "";
4.
5. public Bean(int id, String name) {
6. this.id = id;
7. this.name = name;
8. }
9.}
Bean数据操作类BeanDAO:
1.public class BeanDAO {
2. static boolean isTableExist;
3.
4. SQLiteDatabase db;
5.
6. public BeanDAO() {
7. this.db = new DbHelper(App.getContext(), "Bean").getWritableDatabase();
8. }
9.
10. /**
11. * 插入Bean
12. */
13. public void insert(Bean bean) {
14. checkTable();
15.
16. ContentValues values = new ContentValues();
17. values.put("id", bean.getId());
18. values.put("name", bean.getName());
19.
20. db.insert("Bean", "", values);
21. }
22.
23. /**
24. * 获取对应id的Bean
25. */
26. public Bean get(int id) {
27. checkTable();
28.
29. Cursor cursor = null;
30.
31. try {
32. cursor = db.rawQuery("SELECT * FROM Bean", null);
33.
34. if (cursor !
= null && cursor.moveToNext()) {
35. String name = cursor.getString(cursor.getColumnIndex("name"));
36.
37. return new Bean(id, name);
38. }
39. } catch (Exception e) {
40. e.printStackTrace();
41. } finally {
42. if (cursor !
= null) {
43. cursor.close();
44. }
45. cursor = null;
46. }
47. return null;
48. }
49.
50. /**
51. * 检查表是否存在,不存在则创建表
52. */
53. private void checkTable() {
54. if (!
isTableExist()) {
55. db.execSQL("CREATE TABLE IF NOT EXISTS Bean ( id INTEGER PRIMARY KEY, name )");
56. }
57. }
58.
59. private boolean isTableExist() {
60. if (isTableExist) {
61. return true; // 上次操作已确定表已存在于数据库,直接返回true
62. }
63.
64. Cursor cursor = null;
65. try {
66. String sql = "SELECT COUNT(*) AS c FROM sqlite_master WHERE type ='table' AND name ='Bean' ";
67.
68. cursor = db.rawQuery(sql, null);
69. if (cursor !
= null && cursor.moveToNext()) {
70. int count = cursor.getInt(0);
71. if (count > 0) {
72. isTableExist = true; // 记录Table已创建,下次执行isTableExist()时,直接返回true
73. return true;
74. }
75. }
76. } catch (Exception e) {
77. e.printStackTrace();
78. } finally {
79. if (cursor !
= null) {
80. cursor.close();
81. }
82. cursor = null;
83. }
84. return false;
85. }
86.}
以上是你在项目中用到的类,当然数据库一般开发者都会用第三方库,例如:
greenDAO、ormlite、dbflow、afinal、xutils....这里考虑到代码演示规范性、通用性,就直接用android提供的SQLiteDatabase。
大家注意到BeanDAO的构造函数:
1.public BeanDAO() {
2. this.db = new DbHelper(App.getContext(), "Bean").getWritableDatabase();
3.}
这种在内部创建对象的方式,不利于单元测试。
App是项目本来的Application,但是使用Robolectric往往会指定一个测试专用的Application(命名为RoboApp,配置方法下面会介绍),这么做好处是隔离App的所有依赖。
隔离原Application依赖
项目原本的App:
1.public class App extends Application {
2.
3. private static Context context;
4.
5. @Override
6. public void onCreate() {
7. super.onCreate();
8. context = this;
9.
10. // 各种第三方初始化,有很多依赖
11. ...
12. }
13.
14. public static Context getContext() {
15. return context;
16. }
17.}
而单元测试使用的RoboApp:
1.public class RoboApp extends Application {}
如果用Robolectric单元测试,不配置RoboApp,就会调用原来的App,而App有很多第三方库依赖,常见的有static{Library.load()}静态加载so库。
于是,执行App生命周期时,robolectric就报错了。
正确配置Application方式,是在单元测试XXTest加上@Config(application=RoboApp.class)。
改进DAO类
1.public class BeanDAO {
2. SQLiteDatabase db;
3.
4. public BeanDAO(SQLiteDatabase db) {
5. this.db = db;
6. }
7.
8. // 可以保留原来的构造函数,只是单元测试不用这个方法而已
9. public BeanDAO() {
10. this.db = new DbHelper(App.getContext(), "Bean").getWritableDatabase();
11. }
单元测试
DAOTest
1.@RunWith(RobolectricTestRunner.class)
2.@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = Build.VERSION_CODES.JELLY_BEAN, application = RoboApp.class)
3.public class DAOTest {
4.
5. BeanDAO dao;
6.
7. @Before
8. public void setUp() throws Exception {
9. // 用随机数做数据库名称,让每个测试方法,都用不同数据库,保证数据唯一性
10. DbHelper dbHelper = new DbHelper(RuntimeEnvironment.application, new Random().nextInt(1000) + ".db");
11. SQLiteDatabase db = dbHelper.getWritableDatabase();
12.
13. dao = new BeanDAO(db);
14. }
15.
16. @Test
17. public void testInsertAndGet() throws Exception {
18. Bean bean = new Bean(1, "键盘男");
19.
20. dao.insert(bean);
21.
22. Bean retBean = dao.get
(1);
23.
24. Assert.assertEquals(retBean.getId(), 1);
25. Assert.assertEquals(retBean.getName(), "键盘男");
26. }
27.}
DAO单元测试跟Presenter有点不一样,可以说会更简单、直观。
Presenter单元测试会用mock去隔离一些依赖,并且模拟返回值,但是sqlite执行是真实的,不能mock的。
正常情况,insert()和get()应该分别测试,但这样非常麻烦,必然要在测试用例写sqlite语句,并且对SQLiteDatabase操作。
考虑到数据库操作的真实性,笔者把insert和get放在同一个测试用例:
如果insert()失败,那么get()必然拿不到数据,testInsertAndGet()失败;只有insert()和get()代码都正确,testInsertAndGet()才能通过。
由于用Robolectric,所以单元测试要比直接junit要慢。
仅junit跑单元测试,耗时基本在毫秒(ms)级,而robolectric则是秒级(s)。
不过怎么说也比跑真机、模拟器的单元测试要快很多。
SharedPreference
其实,SharedPreference道理跟sqlite一样,也是对每个测试用例创建单独SharedPreference,然后保存、查找一起测。
ShareDAO:
1.public class ShareDAO {
2. SharedPreferences sharedPref;
3. SharedPreferences.Editor editor;
4.
5. public ShareDAO(SharedPreferences sharedPref) {
6. this.sharedPref = sharedPref;
7. this.editor = sharedPref.edit();
8. }
9.
10. public ShareDAO() {
11. this(App.getContext().getSharedPreferences("myShare", Context.MODE_PRIVATE));
12. }
13.
14. public void put(String key, String value) {
15. editor.putString(key, value);
16. editor.apply();
17. }
18.
19. public String get(String key) {
20. return sharedPref.getString(key, "");
21. }
22.}
单元测试ShareDAOTest
1.@RunWith(RobolectricTestRunner.class)
2.@Config(constants = BuildConfig.class, manifest = Config.NONE, sdk = Build.VERSION_CODES.JELLY_BEAN, application = RoboApp.class)
3.public class ShareDAOTest {
4.
5. ShareDAO shareDAO;
6.
7. @Before
8. public void setUp() throws Exception {
9. String name = new Random().nextInt(1000) + ".pref";
10.
11. shareDAO = new ShareDAO(RuntimeEnvironment.application.getSharedPreferences(name, Context.MODE_PRIVATE));
12. }
13.
14. @Test
15. public void testPutAndGet() throws Exception {
16. shareDAO.put("key01", "stringA");
17.
18. String value = shareDAO.get("key01");
19.
20. Assert.assertEquals(value, "stringA");
21. }
22.}
测试通过了。
是不是很简单?
Assets
Robolectric对Assets支持也是相当不错的,测Assets道理也是跟sqlite、sharePreference相同。
/assets/test.txt:
1.success
1.public class AssetsReader {
2.
3. AssetManager assetManager;
4.
5. public AssetsReader(AssetManager assetManager) {
6. this.assetManager = assetManager;
7. }
8.
9. public AssetsReader() {
10. assetManager = App.getContext()
11. .getAssets();
12. }
13.
14. public String read(String fileName) {
15. try {
16. InputStream inputStream = assetManager.open(fileName);
17.
18. StringBuilder sb = new StringBuilder();
19.
20. byte[] buffer = new byte[1024];
21.
22. int hasRead;
23.
24. while ((hasRead = inputStream.read(buffer, 0, buffer.length)) > -1) {
25. sb.append(new String(buffer, 0, hasRead));
26. }
27.
28. inputStream.close();
29.
30. return sb.toString();
31. } catch (IOException e) {
32. e.printStackTrace();
33
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 单元测试