How to mock PreferenceManager in Android?
Asked Answered
A

2

18

I've written a class that is using Context, a third party library and SharedPreferences from PreferenceManager.

It's possible to mock Context, the third party library can be mocked using some mocking framework, but what to do with PreferenceManager?

I have two methods:

public void saveString(ThirdPartyObject obj) {
    SharedPreferences appPreferences = 
        PreferenceManager.getDefaultSharedPreferences(mContext);
    SharedPreferences.Editor editor = appPreferences.edit();
    editor.putString(mContext.getString(
        R.string.preferences_string_name), obj.getString());
    editor.commit();
}

and corresponding, that loads preferences.

Afrikander answered 26/7, 2010 at 17:58 Comment(2)
Is there any particular behaviour in PreferenceManager that you need to mock? Otherwise testing preference code works fine from an AndroidTestCase.Lucilelucilia
It's a shame that I can't accept a comment - but what you've written satisfies me.Afrikander
A
5

It doesn't look like you actually want a mock instance of PreferenceManager (which is mostly used in a PreferenceFragment or PreferenceActivity).

You probably want either:

  1. A mock SharedPreferences, in which case you can just mock Context#getSharedPreferences (which is called by PreferenceManager#getDefaultSharedPreferences anyhow). You'll probably also have to make a mock SharedPreferences.Editor if preferences are edited, as above. You say you already know how to mock the context, so this should be fairly straightforward.

  2. To use the actual preferences in the environment. This is easiest, and not necessarily a bad idea. Do make certain it's cleaned up properly so that your tests don't interfere with each other (or, depending on your test environment, aren't affected by manual use of the app).

If you really do want to mock PreferenceManager instance (like that you get in PreferenceFragment or PreferenceActivity), you can absolutely do so.

Since it's non-final, you can generate a mock PreferenceManager and SharedPreferences using Mockito (or another mocking library) as long as you have a way to provide it to your code wherever you would ordinarily get one (in non-test code, this normally comes from the getPreferenceManager()).

Abagael answered 7/3, 2013 at 7:2 Comment(1)
Context#getSharedPreferences requires you to pass a file name.Anorthic
A
3

You can use specialized context for shared preference. RenamingDelegatingContext delegates everything to a Context. When we access SharedPreference from a Context, we use getSharedPreferences(String name, int mode).

Here by extending RenamingDelegatingContext we override getSharedPreferences and pretend the name parameter with test PREFIX, So when test runs it will write to preference file which is different then main application.

public class SpecializedMockContext extends RenamingDelegatingContext {
    public static final String PREFIX = "test.";

    public SpecializedMockContext(Context context) {
        super(context, PREFIX);
    }

    @Override
    public SharedPreferences getSharedPreferences(String name, int mode) {
        return super.getSharedPreferences(PREFIX + name, mode);
    }
}

Set this SpecialisedMockContext to your test Application context. setContext(specialisedMockContext) and createApplication().

Anticipant answered 6/2, 2016 at 4:47 Comment(2)
I'm not sure why this answer isn't getting any upvotes; I've added mine. It certainly documents what I've done in the past and has worked well for me. If you've got a limited stable of real devices, as most of us do, then not-polluting a working set of preferences is important for test code.Petronel
Where do you call setContext(specialisedMockContext)?Anorthic

© 2022 - 2024 — McMap. All rights reserved.