40

你如何对 Parcelable 进行单元测试?我创建了一个 Parcelable 类,并编写了这个单元测试

TestClass test = new TestClass();
Bundle bundle = new Bundle();
bundle.putParcelable("test", test);

TestClass testAfter = bundle.getParcelable("test");
assertEquals(testAfter.getStuff(), event1.getStuff());

我故意尝试通过在 中返回 null 来使测试失败createFromParcel(),但它似乎成功了。看起来它在需要之前不会被打包。如何强制捆绑到..捆绑?

4

4 回答 4

99

我发现此链接显示如何对可打包对象进行单元测试:http: //stuffikeepforgettinghowtodo.blogspot.nl/2009/02/unit-test-your-custom-parcelable.html

Bundle如果你真的不需要像 zorch 的建议那样包含它,你实际上可以跳过它。然后你会得到这样的东西:

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

    // 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);
}
于 2013-01-27T14:19:54.790 回答
19

你可以这样做:

//Create parcelable object and put to Bundle
    Question q = new Question(questionId, surveyServerId, title, type, answers);
    Bundle b = new Bundle();
    b.putParcelable("someTag", q);

//Save bundle to parcel
    Parcel parcel = Parcel.obtain();
    b.writeToParcel(parcel, 0);

//Extract bundle from parcel
    parcel.setDataPosition(0);
    Bundle b2 = parcel.readBundle();
    b2.setClassLoader(Question.class.getClassLoader());
    Question q2 = b2.getParcelable("someTag");

//Check that objects are not same and test that objects are equal
    assertFalse("Bundle is the same", b2 == b);
    assertFalse("Question is the same", q2 == q);
    assertTrue("Questions aren't equal", q2.equals(q));
于 2012-11-22T06:45:44.403 回答
6

由于这个问题和答案帮助了我好几年,我想我会添加我自己的建议,那就是assert阅读dataPosition()结束时的 the 与写作结束时的相同。基于Xilconic 的回答

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

背景:这是在花了(令人尴尬的)时间调试一个 bad Parcelable. 就我而言,writeToParcel是从一个中等复杂的对象图中的一个对象编写一个重复的字段。因此,随后的对象被错误地读取,给出了误导性的异常,并且在使用特定行为不端的对象的测试中没有错误。

追踪起来很痛苦,然后我意识到检查dataPosition会更快地查明问题,因为我对内部对象和主容器进行了测试。


Kotlin:因为我在 Kotlin 工作,所以有点 lambda 和具体化魔法:

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
}

我现在可以很容易地按照这些思路进行测试,如果有一个完整且可靠的equals()

testObject.parcel.test { assertEquals(testObject, it) }

请注意,避免使用此答案.parcel.test重新指定泛型类型参数。

或者,对于更复杂的断言:

testObject.parcel.test {
    assertEquals(123, it.id)
    assertEquals("dewey", it.name)
    // ...
}
于 2018-01-10T19:25:57.133 回答
0

我正在使用包裹助手类将对象转换为包裹。

看到这个要点:https ://gist.github.com/scallacs/f749a7385bcf75829a98d7b651efd02e

用法很简单:

Model model = new Model("HelloWorld");
// Create an object from the parcel model
Model createdFromParcel = ParcelTestHelper.createFromParcel(model, model.CREATOR);

// Do your assertions...
assertEquals(model.value, createdFromParcel.value);

实施:

import android.os.Parcel;
import android.os.Parcelable;

public class ParcelTestHelper {

    public static String DEFAULT_CREATOR_FIELD = "CREATOR";

    public static <T extends Parcelable> T createFromParcel(T input, Parcelable.Creator<T> creator) {
        Parcel parcel = toParcel(input);
        return fromParcel(parcel, creator);
    }

    public static <T extends Parcelable> T createFromParcel(T input) throws NoSuchFieldException, IllegalAccessException {
        return createFromParcel(input, getCreator(input));
    }

    public static <T extends Parcelable> Parcel toParcel(T input) {
        Parcel parcel = Parcel.obtain();
        input.writeToParcel(parcel, input.describeContents());
        parcel.setDataPosition(0);
        return parcel;
    }

    public static <T> Parcelable.Creator<T> getCreator(T input) throws NoSuchFieldException, IllegalAccessException {
        return getCreator(input, DEFAULT_CREATOR_FIELD);
    }

    public static <T> Parcelable.Creator<T> getCreator(T input, String field) throws NoSuchFieldException, IllegalAccessException {
        Object creator = input.getClass().getField(field).get(input);
        if (!(creator instanceof Parcelable.Creator)) {
            throw new InternalError("Should have field " + field + " instance of Parcelable.Creator");
        }
        return (Parcelable.Creator<T>) creator;
    }

    public static <T extends Parcelable> T fromParcel(Parcel parcel, Parcelable.Creator<T> creator) {
        return creator.createFromParcel(parcel);
    }
}
于 2018-05-17T08:12:05.413 回答