#android #kotlin #junit #mockito #sharedpreferences
Вопрос:
Я пишу тестовые примеры JUnit для sharedpreferences, как показано ниже
class SharedPrefTest {
private lateinit var context: Context
private lateinit var userRepo: UserRepo
private lateinit var sharedPreferences: SharedPreferences
@Before
fun setup() {
context = mock()
userRepo = UserRepo(context)
}
@Test
fun `check mocked instances are not null`() {
context assertNotEquals null //mocked successfully
userRepo assertNotEquals null
} //PASS
@Test
fun `when shared pref instance not null then do further ops`() {
sharedPreferences = context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE)
sharedPreferences assertNotEquals null
} //FAIL
companion object {
const val USER_PREFS = "userprefs"
}
}
Тем не менее, я получаю сообщение об ошибке ниже.
context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE) must not be null
java.lang.NullPointerException: context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE) must not be null
Я уже проверил, что context не равен null в отдельном модульном тесте, но все равно его сбой.
Комментарии:
1. Я не понимаю проблемы! Вы уже издевались над контекстом. Почему он должен возвращать не заглушенный (ненулевой) ответ на getSharedPreferences?
2. Используйте Robolectric для написания модульных тестов. Вам не нужно будет вручную имитировать зависимости платформы Android, такие как Context.
3. Проблема @ashu заключается в том, что он не создает экземпляр sharedpreference и выдает нулевую ошибку. Кроме того, ограничено использование Mockito
Ответ №1:
Проблема в том, что он не создает экземпляр sharedpreference и выдает ошибку null.
Почему издевательский объект вызывает его конкретную реализацию? Макет объекта — это заглушка, поведение которой предварительно настроено для конкретных тестовых сценариев. Ожидается, что во время тестов клиенты будут явно указывать, что произойдет, если определенный метод будет вызван для издевательского объекта. Если клиенты не предоставляют такую спецификацию, издевающийся объект часто по умолчанию возвращает нулевые значения для методов, которые не возвращают void; null
в случае Mockito (точнее, Java).
Следовательно, вы издеваетесь над context
, следовательно, context.getSharedPreferences()
возвращает null
значение по умолчанию. Поскольку sharedPreferences
поле объявлено как тип, не допускающий обнуления Kotlin, присвоение null
ему выдает NPE. Вы можете исправить это, вернув издевательский SharedPreferences
экземпляр из context.getSharedPreferences()
.
@Test
fun `when shared pref instance not null then do further ops`() {
val mockPrefs: SharedPreferences = mock()
`when`(context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE))
.thenReturn(mockPrefs);
sharedPreferences = context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE)
sharedPreferences assertNotEquals null
}
Лучшим подходом было бы использовать платформу модульного тестирования для Android, такую как Robolectric.
import androidx.test.core.app.ApplicationProvider
@RunWith(RobolectricTestRunner::class)
class SharedPrefTest {
private lateinit var context: Context
private lateinit var userRepo: UserRepo
private lateinit var sharedPreferences: SharedPreferences
@Before
fun setup() {
context = ApplicationProvider.getApplicationContext()
userRepo = UserRepo(context)
}
@Test
fun `check mocked instances are not null`() {
context assertNotEquals null
userRepo assertNotEquals null
}
@Test
fun `when shared pref instance not null then do further ops`() {
sharedPreferences = context.getSharedPreferences(USER_PREFS, Context.MODE_PRIVATE)
sharedPreferences assertNotEquals null
}
companion object {
const val USER_PREFS = "userprefs"
}
}