如何模拟 Parcel.recycle()?

How to mock Parcel.recycle()?

在我们的 Android 应用程序中,我们管理医疗案例,该应用程序部分使用 Kotlin 编写。用户可以编辑它们。我们通过在演示者中创建一个克隆来验证更改,仅更改副本并将其与原始文件进行比较。我使用 Parcelable 克隆案例对象如下:

public Case cloneObject() {
    Parcel parcel = null;
    try {
        parcel = Parcel.obtain();
        parcel.writeParcelable(this, 0);
        parcel.setDataPosition(0);
        return parcel.readParcelable(Case.class.getClassLoader());
    } finally {
        if (parcel != null) {
            parcel.recycle();
        }
    }
}

在应用程序中,我们会像这样验证对案例的更改:

override fun validateHasChanges(): Boolean {
    return !(model.updatedCase.title == model.originalCase.title &&
            model.updatedCase.description == model.originalCase.description &&
            model.updatedCase.category == model.originalCase.category &&
            model.updatedCase.visibility == model.originalCase.visibility &&
            areCaseFilesEqual() &&
            model.updatedCase.recommendedFor == model.originalCase.recommendedFor &&
            !model.wasConsentClicked &&
            !model.wasImagesClicked)
}

现在我想写一个单元测试来验证上面的正确性。问题是 Parcel 有静态方法和最终方法,还有一个最终无效方法 recycle()。我使用 PowerMockito 克服了其中的大部分问题,但在花了好几天时间后,我无论如何都无法模拟回收。这是目前的样子。

@RunWith(PowerMockRunner::class)
@PrepareForTest(Case::class, Parcel::class)
class QuickPostTest : BehaviorSpec() {

@Test
fun `validate has changes when category is different`() {
    PowerMockito.mockStatic(Parcel::class.java)
    val spy = PowerMockito.spy(Case())
    val spy2 = PowerMockito.mock(Parcel::class.java)
    PowerMockito.doNothing().`when`(spy2.recycle())
    //suppress(method(Parcel::class.java, "recycle"))
    Mockito.`when`(Parcel.obtain()).thenReturn(Whitebox.newInstance(Parcel::class.java))
    Mockito.`when`(spy.cloneObject()).thenReturn(cloneObject(spy))
    val originalCase = Case()
    originalCase.title = "Cool"
    val specialty = Specialty()
    specialty.id = "anaesthetics"
    val specialty2 = Specialty()
    specialty2.id = "anaesthetics.anaesthesia.cardiothoracic"
    originalCase.addSpecialty(specialty)
    val model = BaseCreateCaseModel(originalCase, true)
    model.updatedCase.addSpecialty(specialty2)
    val presenter = CreateCaseQuickPresenter(originalCase)
    presenter.setModel(model)
    assertTrue(presenter.validateHasChanges())
}

private fun cloneObject(aCase: Case): Case {
    var parcel: Parcel? = null
    try {
        parcel = Parcel.obtain()
        parcel!!.writeParcelable(aCase, 0)
        parcel.setDataPosition(0)
        return parcel.readParcelable(Case::class.java.classLoader)
    } finally {
        if (parcel != null) {
            parcel.recycle()
        }
    }
}

}

我应该如何修改它才能使其工作?

这不是我问题的直接答案,但我相信它是正确的。我选择 Parcel 进行克隆是因为我确信它是最简单的解决方案,因为 Java 中的 Cloneable 接口已损坏并且复制构造函数太复杂了。但是我错了。将 Parcelable 添加到我的模型中,并在我的 Presenter 中添加 Android 依赖项。事实证明,这种情况下的复制构造函数并没有那么复杂,即使我的 class 层次结构非常深。所以我这样做了,删除了间谍,现在可以使用了。我不得不保留 PowerMockito,因为我需要 Parcelable 来传递 Case 对象,但我在 Presenter 中不需要它,所以 mockStatic 就足够了。

测试结果如下:

@RunWith(PowerMockRunner::class)
@PrepareForTest(Case::class, Parcel::class)
class QuickPostTest : BehaviorSpec() {

   @Test
   fun `validate has changes when category is different`() {
      PowerMockito.mockStatic(Parcel::class.java)
      val originalCase = Case()
      originalCase.title = "Cool"
      val specialty = Specialty()
      specialty.id = "anaesthetics"
      specialty.isActive = true
      val specialty2 = Specialty()
      specialty2.id = "anaesthetics.anaesthesia.cardiothoracic"
      specialty2.isActive = true
      originalCase.addSpecialty(specialty)
      val model = BaseCreateCaseModel(originalCase, true)
      model.updatedCase.addSpecialty(specialty2)
      val presenter = CreateCaseQuickPresenter(originalCase)
      presenter.setModel(model)

      assertTrue(presenter.validateHasChanges())
  }
}