Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock PreferenceManager with Mockito

I am new in Android unit testing and I want to add some unit tests in an existing project. I am using the MVP design architecture. Inside my presenter, I have a call to PreferenceManager in order to get the default SharedPrefences but it always returns null. I have followed some tutorials and advices across stackoverflow on how to mock PreferenceManager and SharedPreferences but I can't make it work. This is my presenter class

@RunWith(MockitoJUnitRunner.class)
public class SettingsPresenterTest {
    @Mock
    private SettingsView mView;
    @Mock
    private LocalConfiguration conf;
    private SettingsPresenter mPresenter;

    public SettingsPresenterTest() {
        super();
    }

    @Before
    public void startUp() throws Exception {
        MockitoAnnotations.initMocks(LocalConfiguration.class);
        MockitoAnnotations.initMocks(PreferenceManager.class);


        mPresenter = new SettingsPresenter(mView);


        final SharedPreferences sharedPrefs = 
        Mockito.mock(SharedPreferences.class);
        final Context context = Mockito.mock(Context.class);

       Mockito.when(PreferenceManager.getDefaultSharedPreferences(context)).
       thenReturn(sharedPrefs);
    }


    @Test
    public void notificationsEnabledClicked() throws Exception {
        boolean notifsEnabled = false;
        mPresenter.notificationsEnabledClicked(notifsEnabled);

        Mockito.verify(mView).setNotificationsView(notifsEnabled);
    }
}

and here is the method where the SharedPreferences are returned null

public class LocalConfiguration { 
    public TerritoryDto getLastSavedTerritory() {
           SharedPreferences preferences = 
           PreferenceManager.getDefaultSharedPreferences(H.getContext());
           String terrritoryString = preferences.getString(SAVED_TERRITORY, 
           null);
           return 
           SerializerHelper.getInstance().deserialize(terrritoryString, 
           TerritoryDto.class);
    }
}

Could you give me some guidelines on how to resolve this error?

like image 283
antonis_st Avatar asked Oct 18 '22 04:10

antonis_st


1 Answers

Instead of directly referring to Android SDK, abstract that out from your presenter logics. What this means is, that instead of performing PreferenceManager.getDefaultSharedPreferences(), create an abstraction and ask for territoryString from your abstraction.

What will this give to you, is that your presenter won't know about the precense of neither PreferenceManager nor SharedPreferences, which are from Android SDK, thus you would have enough seams to perform pure unit testing.

Having said this, let's implement the abstractions. Having declared following interface:


    public interface Storage {
        @Nullable
        String getSavedTerritory();
    }

To which the concrete implementation would be:


    public SharedPrefsStorage implements Storage {

        private final SharedPreferences prefs;

        public SharedPrefsStorage(Context context) {
            prefs = PreferenceManager.getDefaultSharedPreferences();
        }

        @Nullable
        @Override
        public String getSavedTerritory() {
            return prefs.getString(SAVED_TERRITORY, null);
        }

    }

Then your presenter would become something like this:


    public class LocalConfiguration { 

        final Storage storage;

        public LocalConfiguration(Storage storage) {
            this.storage = storage;
        }

        public TerritoryDto getLastSavedTerritory() {
               final String territory = storage.getSavedTerritory();

               return SerializerHelper.getInstance().deserialize(territory, TerritoryDto.class);
        }
    }

This would give you a seam to perform pure unit testing:


    @Test
    void someTest() {
        when(storage.getSavedTerritory()).thenReturn("New York");
        ...
    }

No need to worry about mocking PreferenceManager anymore.

like image 187
azizbekian Avatar answered Oct 27 '22 11:10

azizbekian