【问题标题】:Android Unit Testing: Bundle/ParcelableAndroid 单元测试:Bundle/Parcelable
【发布时间】:2012-10-01 12:51:18
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如果我们的任何一个答案可以解决您的问题,请将它们标记为已接受的答案:)

标签: android unit-testing bundle parcelable


【解决方案1】:

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

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

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

【讨论】:

  • parcel.setDataPosition(0); 是秘密。谢谢
  • 也适合我。谢谢!。
  • 没有机会工作!你将永远得到BadParcelableException
  • @ddmytrenko BadParcelableException 当对象本身被错误地打包时发生。例如当你的读写对象顺序不同时
【解决方案2】:

你可以这样做:

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

【讨论】:

  • 您是否尝试过这种断言:Assert.assertTrue(EqualsBuilder.reflectionEquals(q2, q));?我问是因为如果您在 Question.java 数据模型中有一个 List<obj> = new ArrayList<obj> 作为字段,那么您编写的代码将不起作用。如果你有ArrayList<obj> = new ArrayList<obj> 它可以工作。你知道如何解决这个问题吗?
【解决方案3】:

由于这个问题和答案在几年后帮助了我,我想我会添加我自己的建议,即 assert 阅读末尾的 dataPosition() 与写结束。建立在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());
}

背景:这是在我花了(令人尴尬的)时间调试一个糟糕的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 避免了使用this answer 重新指定泛型类型参数。

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

testObject.parcel.test {
    assertEquals(123, it.id)
    assertEquals("dewey", it.name)
    // ...
}

【讨论】:

    【解决方案4】:

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

    请参阅此要点: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);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多