我尝试对我的演示者进行单元测试。我有更新标记的方法,当它失败时,应用程序会显示带有翻译消息的吐司。
override fun updateMark(cameraId: Int, markId: Int, createData: MarkCreateData, context: Context) {
view?.let {
view?.showOrHideProgressBar(true)
apiManager.updateMark(cameraId, markId, createData)
.subsIoObsMain()
.subscribe({
view?.showOrHideProgressBar(false)
view?.updateMarkSuccess()
}) {
view?.showOrHideProgressBar(false)
view?.showToast(getStringForLayoutByKey("mark_updated_fail"))
}.addTo(compositeDisposable)
}
}
提供翻译的方法包含在使用 prefs 的 LocalData 对象中。
object LocalData {
private val prefs: SharedPreferences = App.instance.getSharedPreferences(this.javaClass.simpleName, Context.MODE_PRIVATE)
fun getStringForLayoutByKey(key: String) =
if (getJsonDataServer().getByKeyOrKeySave(key).isNullOrEmpty()) {
getJsonDataClient().getByKeyOrKeySave(key) ?: ""
} else {
getJsonDataServer().getByKeyOrKeySave(key) ?: ""
}
}
将 init 与 App.instance 放在一起,它是伴随的 lateinit var。
class App @Inject constructor(): Application(), LifecycleObserver {
companion object {
@JvmStatic
lateinit var instance: App
@JvmField
var localData: LocalData? = null
}
override fun onCreate() {
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
super.onCreate()
instance = this
localData = LocalData
buildDagger()
}
}
所以我尝试运行我的单元测试。
@RunWith(MockitoJUnitRunner::class)
class PlayerPresenterTest {
@Rule
@JvmField
val rule = ImmediateSchedulerRule()
@Mock
lateinit var apiManager: NetworkManager
@Mock
lateinit var playerFragment: PlayerContract.View
@Mock
lateinit var context: Context
@Mock
lateinit var unknownError: UnknownHostException
@Mock
lateinit var localData: LocalData
@Mock
lateinit var sharedPreferences: SharedPreferences
@Mock
lateinit var app: App.Companion
@Mock
lateinit var instance: App
//variables
private lateinit var playerPresenter: PlayerPresenter
private lateinit var markCreateData: MarkCreateData
private val errorMarkUpdate = "mark updated fail. try later"
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
MockKAnnotations.init(this, relaxUnitFun = true)
playerPresenter = PlayerPresenter()
playerPresenter.attachView(playerFragment)
playerPresenter.apiManager = apiManager
markCreateData = MarkCreateData()
}
//try update mark and get error
@Test
fun test_failed_update_mark() {
val error = Observable.error<Mark>(unknownError)
whenever(apiManager.updateMark(cameraId.toInt(), markId.toInt(), markCreateData)).thenReturn(error)
mockkObject(App)
every { App } returns app
every { app.instance } returns instance
every { instance.getSharedPreferences(this.javaClass.simpleName, Context.MODE_PRIVATE) } returns sharedPreferences
every { localData.getStringForLayoutByKey("mark_updated_fail") } returns errorMarkUpdate
playerPresenter.updateMark(cameraId.toInt(), markId.toInt(), markCreateData, context)
verify(playerFragment, atLeastOnce()).showToast(errorMarkUpdate)
}
}
并得到引用 LocalData private val prefs 初始化的错误“原因:kotlin.UninitializedPropertyAccessException:lateinit 属性实例尚未初始化”。smb 可以帮我解决这个问题吗?如何在我的单元测试中初始化实例并避免该错误?或者也许我必须在不使用 App.instance 引用的情况下模拟 LocalData 对象?