【问题标题】:Test SharedPreferences in Android在 Android 中测试 SharedPreferences
【发布时间】:2023-03-08 09:02:01
【问题描述】:

我正在进入一个使用 SharedPreferences 进行数据持久性的遗留代码库。我想测试保存/检索值,使用 MockK 进行模拟。但是,此单元测试中的断言永远不会通过。好像 SharedPrefs 在测试中没有正确存储:

class MyProfilePrefsTest {
  private lateinit var myProfilePrefs: ProfilePrefs
  @RelaxedMockK private lateinit var mockSharedPrefs: SharedPreferences
  @RelaxedMockK private lateinit var context: Context

  @Before
  fun setup() {
    MockKAnnotations.init(this)

    val sharedPreferences = mockk<SharedPreferences>()
    every { sharedPreferences.edit() } returns (mockk())
    myProfilePrefs = ProfilePrefs(context, sharedPreferences)

    mockStatic(DeviceInfo::class)
    every { DeviceInfo.serialNumber() } returns "fake_serial"
}

@Test
fun `Saving correct cellular download pref for device id`() {
    // Arrange
    val isEnabled = true

    // Act
    myProfilePrefs.setCellularDownloadingEnabled(isEnabled)

    // Assert
    assertTrue(myProfilePrefs.getCellularDownloadingEnabled())
}}

有人知道如何对 SharedPrefs 进行单元测试吗?

【问题讨论】:

  • 我认为被嘲笑的SharedPreferences.Editor 是问题所在;因为它是一个模拟,所以调用 commit()apply() 实际上不会做任何事情。
  • 那有必要mockEditor吗?
  • 我没有为 SharedPreferences 编写单元测试的经验。我通常希望您改为使用带有真实首选项存储的仪器测试(您可以根据需要设置和拆除),但即使在那里我也没有真正的经验。

标签: android unit-testing sharedpreferences mockk


【解决方案1】:

您需要Robolectric 库来测试与Context 相关的类。这个库将模拟一个 Android 设备(没有模拟器)。
在这种情况下,您可以使用 RuntimeEnvironment.application.getApplicationContext() 它将返回真实的,而不是 Context 类的模拟对象。

2020 年 5 月更新:

RuntimeEnvironment.application.getApplicationContext() 现在已弃用

请使用ApplicationProvider.getApplicationContext() 获取Context。另外,请记住,您应该将testImplementation 'androidx.test:core:1.2.0' 添加到您的build.gradle

所以 Espresso 也可以帮助您,但它是工具测试。

【讨论】:

  • 我虽然 Robolectric 现在是 AndroidX 测试?
  • 如果你需要使用一些内部的android类比如SharedPrefernces,你需要使用Robolectric的上下文。
【解决方案2】:

感谢以上@samaromku 的建议答案。这是完整的解决方案。它使用 AndroidX Test 运行器:

@RunWith(AndroidJUnit4::class)
class ProfilePrefsTest {
  private lateinit var profilePrefs: ProfilePrefs
  private lateinit var context: Context

  @Before
  fun setup() {
    context = getApplicationContext<MyApplication>()
    val sharedPreferences = context.getSharedPreferences(
        "prefs",
        MODE_PRIVATE
    );
    profilePrefs = ProfilePrefs(context, sharedPreferences)

    mockStatic(DeviceInfo::class)
    every { DeviceInfo.serialNumber() } returns FAKE_SERIAL_NUMBER
  }

  @Test
  fun `Saving correct cellular download pref for device id`() {
    // Arrange
    val isEnabled = true

    // Act
    profilePrefs.setCellularDownloadingEnabled(isEnabled)

    // Assert
    assertTrue(profilePrefs.isCellularDownloadingEnabled())
}

}

【讨论】:

  • 只是在这里问一个问题,但测试共享偏好是不是多余或错误的?我们知道它们可以工作,它是一个 android 组件,而不是我们自己的组件,所以您不应该围绕使用 prefs 测试逻辑而不是实际测试 prefs...?
  • @a_local_nobody 是的,应该只测试他/她编写的代码/业务逻辑。无需测试已经存在并证明可以工作的框架 (Android) 组件。大多数时候,我们的逻辑是导致这些组件做错事的原因,而不是他们的:错误的数据、不正确的方法调用等。在这种情况下,你是对的。不鼓励像这样测试 sharedprefs,应该不惜一切代价避免。
  • 不过!这些是 android 组件,但它是您的代码写入,它是您的代码正在检索。因此,如果您听说过测试驱动开发。我建议像这样写一个测试。作为一个懒惰的开发者,我仍然觉得你是对的。 :D
猜你喜欢
  • 2013-07-21
  • 1970-01-01
  • 2013-09-05
  • 2015-06-16
  • 1970-01-01
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多