Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Parcel.obtain() returns null?

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

like image 285
Michael Schrempp Avatar asked Aug 12 '15 15:08

Michael Schrempp


2 Answers

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.

like image 194
Mikhail Avatar answered Nov 17 '22 13:11

Mikhail


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.

like image 33
milosmns Avatar answered Nov 17 '22 11:11

milosmns