Since this question and answers helped me several years down the line, I thought I'd add my own suggestion, which would be to assert
that the dataPosition()
at the end of the read was the same as at the end of the write. Building on Xilconic's answer:
@Test
public void testTestClassParcelable(){
TestClass test = new TestClass();
// Obtain a Parcel object and write the parcelable object to it:
Parcel parcel = Parcel.obtain();
test.writeToParcel(parcel, 0);
//>>>>> Record dataPosition
int eop = parcel.dataPosition();
// After you're done with writing, you need to reset the parcel for reading:
parcel.setDataPosition(0);
// Reconstruct object from parcel and asserts:
TestClass createdFromParcel = TestClass.CREATOR.createFromParcel(parcel);
assertEquals(test, createdFromParcel);
//>>>>> Verify dataPosition
assertEquals(eop, parcel.dataPosition());
}
Background: This came to me after spending an (embarrassing) amount of time debugging a bad Parcelable
. In my case, writeToParcel
was writing a duplicate field from one object within a moderately complex object graph. Therefore subsequent objects were read incorrectly, giving misleading exceptions, and no errors on tests with the specific misbehaving object.
It was a pain to track down, and then I realized checking dataPosition
would have pinpointed the problem faster since I have tests on inner objects as well as the main container.
Kotlin: Since I am working in Kotlin, a little lambda and reifying magic:
class ParcelWrap<T>(val value: T)
val <T> T.parcel: ParcelWrap<T> get() = ParcelWrap(this)
inline fun <reified T: Parcelable> ParcelWrap<T>.test(
flags: Int = 0,
classLoader: ClassLoader = T::class.java.classLoader,
checks: (T) -> Unit
): T {
// Create the parcel
val parcel: Parcel = Parcel.obtain()
parcel.writeParcelable(this.value, flags)
// Record dataPosition
val eop = parcel.dataPosition()
// Reset the parcel
parcel.setDataPosition(0)
// Read from the parcel
val newObject = parcel.readParcelable<T>(classLoader)
// Perform the checks provided in the lambda
checks(newObject)
// Verify dataPosition
assertEquals("writeToParcel wrote too much data or read didn't finish reading", eop, parcel.dataPosition())
return newObject
}
I can now test very easily along these lines, if there is a complete and reliable equals()
:
testObject.parcel.test { assertEquals(testObject, it) }
Note the .parcel.test
avoids having to re-specify generic type parameter using this answer.
Or, for more complex assertions:
testObject.parcel.test {
assertEquals(123, it.id)
assertEquals("dewey", it.name)
// ...
}