Android Parcel.obtain() 返回 null?

问题描述 投票:0回答:4

我正在开发一个 Android 应用程序(API 15),在尝试编写单元测试时遇到以下问题:

我使用 android.os.Parcel 保存一个类(例如,如果屏幕转动)并将其发送到另一个 Activity。如果我尝试像这样对包裹方法进行单元测试(我从互联网上得到这个):

@Test
public void testParcel() {
    Comment test = new Comment();
    test.setId(testNr0Id);
    test.setComment(testNr0String);


    // Obtain a Parcel object and write the parcelable object to it:
    Parcel parcel = Parcel.obtain();
    test.writeToParcel(parcel, 0);

    // After you're done with writing, you need to reset the parcel for reading:
    parcel.setDataPosition(0);

    // Reconstruct object from parcel and asserts:
    Comment createdFromParcel = Comment.CREATOR.createFromParcel(parcel);
    assertEquals(test, createdFromParcel);
}

然后我得到一个 NullPointerException,因为从 Parcel.obtain() 返回的 Parcel 是 null。 JavaDoc 只是说这个方法“从池中返回一个包裹对象”。现在我的问题是:哪个池?为什么这个 Parcel-Object 为空?有什么建议如何进行此测试运行吗?

感谢您的帮助

迈克尔

java android parcel
4个回答
14
投票

我也遇到过同样的问题。然而,当我将测试从

test
目录移动到
androidTest
目录后,一切都开始正常工作。我认为这是因为
Parcel
类是系统一类,因此也需要对其进行检测。


5
投票

避免完全使用 Robolectric 或仪器,例如如果您只想在不连接任何设备的情况下使用 JUnit,则可以使用 Mockito 来模拟 Parcel 内的所有内容。

我有一个简单的实现,并且在我的许多项目中使用它:

// Mockito is required for this to work
class ParcelFake {

  companion object {
    @JvmStatic fun obtain(): Parcel {
      return ParcelFake().mock
    }
  }

  private var position = 0
  private var store = mutableListOf<Any>()
  private var mock = mock<Parcel>()

  init {
    setupWrites()
    setupReads()
    setupOthers()
  }

  // uncomment when needed for the first time
  private fun setupWrites() {
    val answer = { i: InvocationOnMock ->
      with(store) {
        add(i.arguments[0])
        get(lastIndex)
      }
    }
    whenever(mock.writeByte(anyByte())).thenAnswer(answer)
    whenever(mock.writeInt(anyInt())).thenAnswer(answer)
    whenever(mock.writeString(anyString())).thenAnswer(answer)
    // whenever(mock.writeLong(anyLong())).thenAnswer(answer)
    // whenever(mock.writeFloat(anyFloat())).thenAnswer(answer)
    // whenever(mock.writeDouble(anyDouble())).thenAnswer(answer)
  }

  // uncomment when needed for the first time
  private fun setupReads() {
    val answer = { _: InvocationOnMock -> store[position++] }
    whenever(mock.readByte()).thenAnswer(answer)
    whenever(mock.readInt()).thenAnswer(answer)
    whenever(mock.readString()).thenAnswer(answer)
    // whenever(mock.readLong()).thenAnswer(answer)
    // whenever(mock.readFloat()).thenAnswer(answer)
    // whenever(mock.readDouble()).thenAnswer(answer)
  }

  private fun setupOthers() {
    val answer = { i: InvocationOnMock ->
      position = i.arguments[0] as Int
      null
    }
    whenever(mock.setDataPosition(anyInt()))
      .thenAnswer(answer)
  }
}

基本思想是拥有一个后备存储(即列表)并将所有内容推入其中 - 但您确实需要使用 Mockito 在内部模拟 Parcel 才能使其正常工作。我注释掉了一些数据类型,但我想您会明白的。

这是阅读文档并尝试各种方法的结果。欢迎任何编辑。


0
投票

Parcel 位于 Android 框架中,因此您的测试只能使用 Expresso 作为 Mikhail 注释,或使用 Robolectric 作为测试运行器。

Robolectric 模拟 Android.jar 并允许进行此类测试,但考虑到您将对 Android 代码进行单元测试...


0
投票

你必须将跑步者添加到你的测试类中:

@RunWith(AndroidJUnit4::class)
class TestClass {

}
© www.soinside.com 2019 - 2024. All rights reserved.