Android Parcel.obtain() returns null?
Asked Answered
K

4

7

i am just developing an Android Application (API 15) and got the following Problem, when trying to write a unit test:

I use android.os.Parcel for saving a class (e.g. if the screen is turned around) and send it to another Activity. If i try to unit test the Parcel Method like this (i got this from the internet):

@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);
}

then i get a NullPointerException, because the Parcel returned from Parcel.obtain() is null. The JavaDoc is just saying that this Method "returns an parcel Object from the pool". Now my question is: Which pool? And why is this Parcel-Object null? Any suggestions how to make this test run?

Thanks for your help

Michael

Kippie answered 12/8, 2015 at 15:14 Comment(0)
I
14

I've had the same issue. However after I moved the test from test directory to androidTest directory everything started working fine. I think this is due to the fact that Parcel class is system one, so it needs to be instrumented as well.

Intestine answered 3/11, 2015 at 16:30 Comment(2)
You might not want to move this to Android tests - it would require having devices attached or using Robolectric (or similar tools) which creates a big test dependency you might not like introducing for this one use-case only. So, if you want a JUnit-only solution, you probably want to fake the Parcel implementation on your own and use that in your tests - https://mcmap.net/q/1408989/-android-parcel-obtain-returns-nullMitzvah
Not saying that there is anything wrong with doing it through Android tests with Robolectric, but a simple JUnit solution might suffice for some (my case as well) :)Mitzvah
M
5

To avoid going to Robolectric or instrumentation altogether, e.g. you want to use JUnit only with no devices attached, you can use Mockito to mock everything inside of the Parcel.

I have a simple implementation and I use it across many of my projects:

// 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)
  }
}

The basic idea is to have a backing storage (i.e. a list) and push everything into it - but you do need to mock a Parcel internally using Mockito to get this to work. I commented out some data types, but I assume you'll get the idea.

This is a result of reading through docs and trying out various approaches. Any edits are welcome.

Mitzvah answered 31/5, 2018 at 23:13 Comment(5)
Nice Guide, your kotlin code has some flaws dealing with Nulls but it directed me into the correct approach, thanks! :)Axle
....aaaand it's a 404 now. :(Mcconaghy
@Axle feel free to edit, it's a snippet nowMitzvah
@Mcconaghy see now :)Mitzvah
"whenever" is in face "Mockito.when" in Kotlin ;)Gastongastralgia
B
0

Parcel is in the Android framework, so your test will only work using Expresso as Mikhail comments, or using Robolectric as the test runner.

Robolectric mocks the Android.jar and allow this kind of testing, but consider that you will be unit testing Android code...

Bridgeman answered 5/3, 2018 at 15:1 Comment(1)
Try newer version of Robolectric and see if it better supports Parcel, I saw this as improvement: github.com/robolectric/robolectric/blob/master/shadows/…Dispermous
E
0

You have to add runner to your TestClass :

@RunWith(AndroidJUnit4::class)
class TestClass {

}
Erse answered 19/11, 2023 at 11:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.