2

I recently started coding my really first android project using Android Studio 3.1.2 and SDK 19.

I'm writing unit tests for my components at the moment and use Mockito for mocking android API dependant objects. When I wrote the test for my SessionHandler, a helper class, that manages data stored in SharedPreferences I came across the problem, that, if I want to check, if e. g. mockEdit.remove("my_key") was successful, I didn't know, how to mock the behavior in particular.

This is how I prepare my mocking stuff:

private final Context mockedContext = Mockito.mock(Context.class);
private final SharedPreferences mockedPrefs = Mockito.mock(SharedPreferences.class);
private final SharedPreferences.Editor mockEdit = Mockito.mock(SharedPreferences.Editor.class);
private boolean shouldReturnTestUUID = true;

@Before
public void prepareMocks() {
    Mockito.when(mockedContext.getSharedPreferences(anyString(), anyInt()).thenReturn(mockedPrefs);
    Mockito.when(mockedPrefs.getString("my_key", null)).thenReturn(shouldReturnTestUUID ? "test_UUID" : null);
    //this is the one, I got stuck at
    Mockito.when(mockEdit.remove("my_key")).thenReturn(mockEdit.putString("my_key", null));
}

The method i'm actually testing:

public synchronized static void removeAppInstanceID(Context context) {
    if (appInstanceID != null) {
        SharedPreferences sharedPrefs = context.getSharedPreferences("session", Context.MODE_PRIVATE);
        sharedPrefs.edit().remove("my_key").apply();
    }
}

The test method:

@Test
public void canRemoveUUID() {
    shouldReturnTestUUID = false;
    SessionHandler.removeAppInstanceID(mockedContext);
    assertNull(mockedPreferences.getString("my_key", null));
    shouldReturnTestUUID = true;
}

If I try to run this test I get an UnfinishedStubbingException referring to the line where I want to mock mockEdit.remove("my_key"). Seemingly, the stub doesn't know what to do with mockEdit.putString("my_key", null);.

So my question is, how to mock this method, so I can call mockedPrefs.getString("my_key") and check if the returned value is null? Thanks in forward.

procra
  • 535
  • 4
  • 29

2 Answers2

2

You have two options:

  • Mock SharedPreferences using Robolectric like here: https://stackoverflow.com/a/9755286/1150795. Robolectric is a common tool for unit testing Android applications and Mocking objects from Android SDK
  • You can add additional layer of abstraction and hide saving SharedPreferences behind an interface, which can be mocked with Mockito
Piotr Wittchen
  • 3,853
  • 4
  • 26
  • 39
  • How do I add a layer to hide save SharedPreferences in particular? – procra Oct 15 '18 at 12:00
  • You can create additional interface exposing methods like `putString(...)`, `getString(...`), etc., add its implementation containing direct usage of `SharedPreferences`, which will be used in the app, Mock this interface in unit tests and verify with Mockito that particular methods (`putString(...)`, `getString(...)`, etc.) were called. Nevertheless, Robolectric may be the easiest approach and you may need it during testing your Android app anyway (e.g. to Mock Android `Context` and other Android related stuff). – Piotr Wittchen Oct 15 '18 at 12:10
1

Maybe a unit test (without integration with Android Framework) would be enough. You could only test that the remove() and apply() methods are called.

For that you need to determine the editor (a mock in this case) returned by edit() method

A Kotlin example, using Mockito, below...

@Test
public void canRemoveUUID() {
    // Arrange
    val mockEdit = mock(SharedPreferences.Editor::class.java)
    `when`(mockEdit.remove("my_key"))
        .thenReturn(mockEdit)

    val mockedPrefs = mock(SharedPreferences::class.java)
    `when`(mockedPrefs.edit())
        .thenReturn(mockEdit)

    val mockedContext = mock(Context::class.java)
    `when`(mockedContext.getSharedPreferences("session", Context.MODE_PRIVATE))
        .thenReturn(mockedPrefs)


    // Act  
    SessionHandler.removeAppInstanceID(mockedContext);

    // Assert
    verify(mockEdit, times(1)).remove("my_key")
    verify(mockEdit, times(1)).commit()
}
gehbiszumeis
  • 3,525
  • 4
  • 24
  • 41