SharePreference单元测试超级简单,程序员日常

永利澳门游戏网站 1

永利澳门游戏网站 2

昨日太晚,没写调侃,是前些天见到有人家作弄伸手党问他要东西,他不肯帮对方做,对方就径直骂人。

吐槽Robolectric

假诺您读过小编的《Android单元测量试验 –
Sqlite、SharedPreference、Assets、文件操作
怎么测?》,就理解大家得以用罗布olectric去做SharePreference单元测量检验。但小编更加的以为罗布olectric特别麻烦,首要以下几点:

1.对初学者门槛高
2.周转成效低下

第一次选取罗布olectric的校友,必然会卡在下载注重的手续,这一步让某些同学放任依旧延缓学习robolectric。读者能够参照《加速罗布olectric下载信赖库及原理深入分析》,通透到底消除那几个主题素材。

支持,正是安顿麻烦,从2.x到3.x版本,配置直接改换(其实是尤为轻巧),2.x本子的安顿到3.x版本,就有标题,不得不再一次看官方文书档案怎样布置。一时不知情是改了gradle本子依然什么来头,配置没变,就给你报错,常见的"No such manifest file: buildintermediatesbundlesdebugAndroidManifest.xml"……

有关运维功效,由于罗布olectric是叁个大而全的框架,单元测量检验到UI测验都能做,运维时先剖判、加载一大堆东西,才给你跑测量检验。小编切磋过源码,早先时期分析慢首即使UI方面,假诺只是测SharePreferenceSQLiteDatabase常有无需,就想不精通罗布olectric团队为何不把SharePreferenceSQLiteDatabase安插分离出来,好让单元测量试验跑快一些。

轻松易行实验,跑四个哪些都不做的robolectric test case:

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class)
public class RoboCase {
    @Test
    public void testRobo() {

    }
}

永利澳门游戏网站 3

Robo Test Case

固然你怎么都不做,倒霉意思,罗布olectric就得运维3秒!并且随着工程代码扩张,那几个小时净增。假若跑一个怎么样都不做的Junit单元测量检验,1ms不到。小编本文介绍的点子,跑简单的运作测量检验时间在10永利澳门游戏网站,~一千ms不等,视乎测试代码复杂度,最快比Robolectric快140+倍


下一场就纪念自个儿果壳网私信里遇见个人,他在自家GitHub上没找到as3
flartoolkit完完全全项目,就跑来问作者要,因为代码时代久远,那时只是做的demo所以版本很乱,笔者懒得一个个跑一遍看哪个是不易版本,笔者让对方一向用作者博文里贴的代码,然后那人继续吧啦吧啦说了一大串,无可奈何拉黑

理解SharedPreferences

大家通过Context获取SharedPreferences

Context context;
SharedPreferences sharePref = context.getSharedPreferences("name", Context.MODE_PRIVATE);

getSharedPreferencesnamemode参数,传分化的name获得分歧的SharedPreferences

SharedPreferences源码:

public interface SharedPreferences {

    public interface OnSharedPreferenceChangeListener {
        void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key);
    }

    public interface Editor {
        Editor putString(String key, @Nullable String value);

        Editor putStringSet(String key, @Nullable Set<String> values);

        Editor putInt(String key, int value);

        Editor putLong(String key, long value);

        Editor putFloat(String key, float value);

        Editor putBoolean(String key, boolean value);

        Editor remove(String key);

        Editor clear();

        boolean commit();

        void apply();
    }

    Map<String, ?> getAll();

    @Nullable
    String getString(String key, @Nullable String defValue);

    @Nullable
    Set<String> getStringSet(String key, @Nullable Set<String> defValues);

    int getInt(String key, int defValue);

    long getLong(String key, long defValue);

    float getFloat(String key, float defValue);

    boolean getBoolean(String key, boolean defValue);

    boolean contains(String key);

    Editor edit();

    void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);

    void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener);
}

SharedPreferences实在只是三个接口,我们获取的目的,是后续该接口的android.app.SharedPreferencesImpl。Android
sdk未有提供这些类,读者可观察源码:SharedPreferencesImpl.java。

从功能上看,SharedPreferences固然轻松的kev-value数据库,在app运营时,对SharedPreferences储存、读取数据,会存放在Android手提式有线电话机该app空间的文本里。

单元测量试验思路

第一,单元测量检验原则是各样测量试验用例的数量独立。由此,前二个测量检验用例在SharedPreferences存款和储蓄的多寡,下叁个用例不应该读取到SharedPreferences就从未有过须求真的把数据储存在文件了,只供给存放在jvm内部存储器就丰裕。

既然SharedPreferences的成成效内部存款和储蓄器达成,那么java代码就能够自由达成key-value积攒,原理跟java.util.Map一模一样。

代码达成SharedPreferences

ShadowSharedPreferences:

public class ShadowSharedPreference implements SharedPreferences {

    Editor editor;

    List<OnSharedPreferenceChangeListener> mOnChangeListeners = new ArrayList<>();
    Map<String, Object>                    map                = new ConcurrentHashMap<>();

    public ShadowSharedPreference() {
        editor = new ShadowEditor(new EditorCall() {

            @Override
            public void apply(Map<String, Object> commitMap, List<String> removeList, boolean commitClear) {
                Map<String, Object> realMap = map;

                // clear
                if (commitClear) {
                    realMap.clear();
                }

                // 移除元素
                for (String key : removeList) {
                    realMap.remove(key);

                    for (OnSharedPreferenceChangeListener listener : mOnChangeListeners) {
                        listener.onSharedPreferenceChanged(ShadowSharedPreference.this, key);
                    }
                }

                // 添加元素
                Set<String> keys = commitMap.keySet();

                // 对比前后变化
                for (String key : keys) {
                    Object lastValue = realMap.get(key);
                    Object value     = commitMap.get(key);

                    if ((lastValue == null && value != null) || (lastValue != null && value == null) || !lastValue.equals(value)) {
                        for (OnSharedPreferenceChangeListener listener : mOnChangeListeners) {
                            listener.onSharedPreferenceChanged(ShadowSharedPreference.this, key);
                        }
                    }
                }

                realMap.putAll(commitMap);
            }
        });
    }

    public Map<String, ?> getAll() {
        return new HashMap<>(map);
    }

    public String getString(String key, @Nullable String defValue) {
        if (map.containsKey(key)) {
            return (String) map.get(key);
        }

        return defValue;
    }

    public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
        if (map.containsKey(key)) {
            return new HashSet<>((Set<String>) map.get(key));
        }

        return defValues;
    }

    public int getInt(String key, int defValue) {
        if (map.containsKey(key)) {
            return (Integer) map.get(key);
        }

        return defValue;
    }

    public long getLong(String key, long defValue) {
        if (map.containsKey(key)) {
            return (Long) map.get(key);
        }

        return defValue;
    }

    public float getFloat(String key, float defValue) {
        if (map.containsKey(key)) {
            return (Float) map.get(key);
        }

        return defValue;
    }

    public boolean getBoolean(String key, boolean defValue) {
        if (map.containsKey(key)) {
            return (Boolean) map.get(key);
        }

        return defValue;
    }

    public boolean contains(String key) {
        return map.containsKey(key);
    }

    public Editor edit() {
        return editor;
    }

    /**
     * 监听对应的key值的变化,只有当key对应的value值发生变化时,才会触发
     *
     * @param listener
     */
    @Override
    public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
        mOnChangeListeners.add(listener);
    }

    @Override
    public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
        mOnChangeListeners.remove(listener);
    }

    interface EditorCall {
        void apply(Map<String, Object> map, List<String> removeList, boolean commitClear);
    }

    public class ShadowEditor implements SharedPreferences.Editor {

        boolean commitClear;

        Map<String, Object> map        = new ConcurrentHashMap<>();
        /**
         * 待移除列表
         */
        List<String>        removeList = new ArrayList<>();

        EditorCall mCall;

        public ShadowEditor(EditorCall call) {
            this.mCall = call;
        }

        public ShadowEditor putString(String key, @Nullable String value) {
            map.put(key, value);
            return this;
        }

        public ShadowEditor putStringSet(String key, @Nullable Set<String> values) {
            map.put(key, new HashSet<>(values));
            return this;
        }

        public ShadowEditor putInt(String key, int value) {
            map.put(key, value);
            return this;
        }

        public ShadowEditor putLong(String key, long value) {
            map.put(key, value);
            return this;
        }

        public ShadowEditor putFloat(String key, float value) {
            map.put(key, value);
            return this;
        }

        public ShadowEditor putBoolean(String key, boolean value) {
            map.put(key, value);
            return this;
        }

        public ShadowEditor remove(String key) {
            map.remove(key);
            removeList.add(key);
            return this;
        }

        public ShadowEditor clear() {
            commitClear = true;
            map.clear();
            removeList.clear();
            return this;
        }

        public boolean commit() {
            try {
                apply();
                return true;
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
        }

        public void apply() {
            mCall.apply(map, removeList, commitClear);

            // 每次提交清空缓存数据
            map.clear();
            commitClear = false;
            removeList.clear();
        }
    }
}

SharePreferenceHelper:

public class SharePreferenceHelper {

    public static SharedPreferences newInstance() {
        return new ShadowSharePreference();
    }
}

只供给八个类,筹划职业就大功告成了,特别简单!

跑单元测量检验

BookDAO:

public class BookDAO {

    SharedPreferences        mSharedPre;
    SharedPreferences.Editor mEditor;

    // 单元测试调用,注意声明protected
    protected BookDAO(SharedPreferences sharedPre) {
        this.mSharedPre = sharedPre;
        this.mEditor    = sharedPre.edit();
    }

    // 正常代码调用
    public BookDAO(Context context) {
        this(context.getSharedPreferences("book", Context.MODE_PRIVATE));
    }

    /**
     * 设置某book是否已读
     *
     * @param bookId 书本id
     * @param isRead 是否已读
     */
    public void setBookRead(int bookId, boolean isRead) {
        mEditor.putBoolean(String.valueOf(bookId), isRead);
        mEditor.commit();
    }

    /**
     * book是否已读
     *
     * @param bookId 书本id
     * @return
     */
    public boolean isBookRead(int bookId) {
        return mSharedPre.getBoolean(String.valueOf(bookId), false);
    }
}

BookDAO有多个构造方法,BookDAO(SharedPreferences sharedPre)BookDAO(Context context),由于单元测量试验未有Context,因而一贯开立SharedPreferences对象就可以。

BookDAOTest单元测量试验:

public class BookDAOTest {

    BookDAO bookDAO;

    @Before
    public void setUp() throws Exception {
        bookDAO = new BookDAO(SharePreferenceHelper.newInstance());
    }

    @Test
    public void isBookRead() throws Exception {
        int bookId = 10;

        // 未读
        Assert.assertFalse(bookDAO.isBookRead(bookId));

        // 设置已读
        bookDAO.setBookRead(bookId, true);

        // 已读
        Assert.assertTrue(bookDAO.isBookRead(bookId));
    }
}

永利澳门游戏网站 4

BookDAO Test Case

仅供给12ms,相当慢,况兼不供给任何配置。

进阶

现象测量检验

你当然有BookDAO,后来重构,供给新添也许放任一些格局只怕其余原因,写二个BookDAOV2。这个BookDAOV2BookDAO的数量分享,意味着用同三个SharedPreferences。

单元测量试验怎么写啊?

public class BookDAOV2 {

    SharedPreferences        mSharedPre;
    SharedPreferences.Editor mEditor;

    protected BookDAOV2(SharedPreferences sharedPre) {
        this.mSharedPre = sharedPre;
        this.mEditor = sharedPre.edit();
    }

    public BookDAOV2(Context context) {
        // 与BookDAO使用同一个SharedPreferences
        this(context.getSharedPreferences("book", Context.MODE_PRIVATE));
    }

    public void clearAllRead() {
        mEditor.clear();
        mEditor.commit();
    }
}

测验用例:

public class BookUpdateTest {

    BookDAO   bookDAO;
    BookDAOV2 bookDAOV2;

    @Before
    public void setUp() throws Exception {
        SharedPreferences sharedPref = SharedPreferencesHelper.newInstance();

        bookDAO = new BookDAO(sharedPref);
        bookDAOV2 = new BookDAOV2(sharedPref);
    }

    @Test
    public void testClearAllRead() {
        int bookId = 10;

        // 设置已读
        bookDAO.setBookRead(bookId, true);

        // 已读
        Assert.assertTrue(bookDAO.isBookRead(bookId));

        // DAOV2 清除已读
        bookDAOV2.clearAllRead();

        // 未读
        Assert.assertFalse(bookDAO.isBookRead(bookId));
    }
}

而是如此不太高雅,能否调用SharedPreferencesHelper同一个形式,再次来到同一个SharedPreferences呢?

发表评论

电子邮件地址不会被公开。 必填项已用*标注