1

My classes is written in Kotlin and here is my SharedPreferenceHandler

   class SharedPreferenceHandler(sharedPrefs: SharedPreferences) {

        companion object {
            var mInstance: SharedPreferenceHandler = SharedPreferenceHandler(getPrefs())

            private fun getPrefs(): SharedPreferences {
                return Application.mInstance.getSharedPreferences(
                        "myApp", Context.MODE_PRIVATE)
            }

            fun getInstance(): SharedPreferenceHandler {
                return mInstance
            }
        }

        private var sharedPreferences = sharedPrefs

        var accessToken: String?
            get() = sharedPreferences.getString(SharedPreference.ACCESS_TOKEN.name, null)
            set(token) = sharedPreferences.edit().putString(SharedPreference.ACCESS_TOKEN.name, token).apply()
}

Here is method called in presenter:

 override fun reload(vm: ViewModel) {
        super.updateViewModel(vm) {
           //some stuffs
        }
    }

Here is my test method:

@Test
public void reload() {
    when(SharedPreferenceHandler.Companion.getMInstance().getAccessToken()).thenReturn("234234234234234");

    presenter.reload(viewModel);
}

In handler from super.updateViewModel(vm) I call "SharedPreferenceHandler.mInstance.accessToken!!)"

That is what is thrown:

Caused by: java.lang.IllegalStateException: Application.mInstanc…m", Context.MODE_PRIVATE) must not be null at com.zuum.zuumapp.preferences.SharedPreferenceHandler$Companion.getPrefs(SharedPreferenceHandler.kt:18) at com.zuum.zuumapp.preferences.SharedPreferenceHandler$Companion.access$getPrefs(SharedPreferenceHandler.kt:14) at com.zuum.zuumapp.preferences.SharedPreferenceHandler.(SharedPreferenceHandler.kt:15)

I wanna to get accessToken by calling " SharedPreferenceHandler.mInstance.accessToken!!" in my test class.

Is possible to get that in my test method?

azsoftco
  • 812
  • 1
  • 8
  • 20

2 Answers2

2

You can't use Android SharedPreferences in unit test, but you can mock your method call by this:

Mockito.`when`(SharedPreferenceHandler.mInstance.accessToken).thenReturn("token")

And return what you need.

Mamykin Andrey
  • 1,352
  • 10
  • 13
1

You should not test your code this way. You should create an interface for class you want to mock:

interface MySharedPreferences {
    fun getAccessToken(): String
}

Let your SharedPreferencesHandler implements this interface. Then in your presenter (or other class you want to test) inject dependencies (f.e. by constructor or framework like Dagger/Kodein) into your object. Then there is possibility to easy mock this interface. I assume in @Before you create class you test - and then just pass as param your mocked SharedPreferencesHandler.

Testing things with static dependencies is possible, but is but tricky (and a lot of people consider static dependencies as anti-pattern). How to do it is described here: How to android unit test and mock a static method

Example:

 class MyPresenter(val sp: MySharedPreferences) {
     /* some code here */
     fun validateToken() {
         if (sp.getAccessToken() == "") throw new Exception()
     }
 }  

Like you see sp is injected into this class as parameter. Normally you don't create views/presenters etc. directly in code but by DI framework (like Dagger or Kodein). Anyway, static dependencies are not easy testable. Injected interface-dependencies can be mocked, and you operating not on object, but on behaviors (so it's bigger level of abstraction). So, now in your test all you have to do is:

class MyTest() {

    @Mock lateinit var sharedPreferencesMock: MySharedPreferences
    lateinit var instance: MyPresenter

    @Before
    fun setUp() {
        instance = MyPresenter(sharedPreferencesMock)
    }

    @Test
    fun testSomething() {
        `when`(sharedPreferencesMock.getAccessToken()).thenReturn("myAccessToken")
        /* here is your test body */
    }
} 
Cililing
  • 4,303
  • 1
  • 17
  • 35