4

In my Android project I decided to create wrapper around SharedPreferences(which is basically key-value storage) with following interface

interface Preferences{
    public void saveInt(int value, String key);
    public void saveString(String value, String key);
    public int readInt(String key);
    public String readString(String key);
}

Implemetation uses Android SharedPreferences API, simplified version looks like that:

public class SharedPrefencesWrapper implements Preferences{
    private SharedPreferences mSharedPreferences;

    public SharedPreferencesStorage(Context context) {
        mSharedPreferences = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
    }
    public void saveInt(int value, String key){
        mSharedPreferences.edit().putInt(key, value).commit();
    }
    //The same for all other public methods
}

Now I want to test SharedPreferencesWrapper class, every public method of Preferences class:

public class SharedPreferencesWrapperTest{
    @Test
    public void savesBoolean(){
       Preferences prefs = new SharedPreferencesWrapper(...);
       prefs.saveInt(3, "intKey");
       int savedValue = prefs.readInt("intKey");
       assertEquals(3, savedValue);
    }
}

Now, can the test look like that? Or should I use SharedPreferences API to check saved values in tests? My concern is that to retrieve the value I will use methods of SharedPrefencesWrapper, something doesn't feel right here.

2 Answers 2

3

In theory, you want to ensure the data has been stored using the SharedPreferences API, and so, in theory, you should check that the values stored using your wrapper are available when directly retrieved using the API.

In practice, especially if you intend that all your code should use your wrapper, you might as well only check it can retrieve what it stores, partly because one of the "normal" reasons for a wrapper is to allow for it to change the underlying storage mechanism, so in the future you might decide to store settings in a database, for example.

1
  • 1
    +1 for favoring not writing test that check against its implementaion-details because reimplementing SharedPreferencesWrapper against a database would break a test that tests against the implementaion-detail-SharedPreferences
    – k3b
    Jun 3, 2015 at 10:09
2

technically it is fine to test one function with its inverse-function. Your low level integrationtest integrates your code with the underlying android-function. Since you cannot mock away the underlying android-function you cannot write a pure unit-test (test in in isolation).

But "...something doesn't feel right here." : I agree (at least with your example): does this abstraction and its test really make sense?

i usually do not write unittests for simple getters/setters/wrappers but have a more hight level integration-test that uses these. writing unittests for getters/setters/wrappers is too much work for too few benefit.

I donot see much benefit for the abstraction of SharedPreferences that still uses "magic numbers" like "intKey" or it-s corresponding constant.

In my opinion a more high-level abstration has more benefit. ie. for settings i would use

MySettingsViaSharedPreferences implements MySettings {
   boolean isDatabaseEnabled() {...}
   string getDatabaseUrl() {...}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.