web-dev-qa-db-ja.com

Androidユニットテスト:バンドル/パーセル可能

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を返すことで意図的にテストに失敗しようとしましたが、成功したようです。必要になるまで分割されないようです。バンドルを強制的にバンドルするにはどうすればよいですか?

31

パーセル可能なオブジェクトをユニットテストする方法を示すこのリンクを見つけました: 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);
}
86
Xilconic

あなたはこの方法でそれを行うことができます:

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

この質問と回答は数年後に私を助けてくれたので、私は自分自身の提案を追加したいと思いました。それは、読み取りの最後のdataPosition()assertであるということです。書き込み終了時と同じです。 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は、適度に複雑なオブジェクトグラフ内の1つのオブジェクトから重複フィールドを書き込んでいました。したがって、subsequentオブジェクトが誤って読み取られ、誤解を招く例外が発生し、特定の誤動作オブジェクトを使用したテストでエラーが発生しませんでした。

追跡するのは大変でしたが、内部オブジェクトとメインコンテナーのテストを行っているため、dataPositionをチェックすると問題をより迅速に特定できることに気付きました。


Kotlin:私はKotlinで作業しているので、小さなラムダと具体化の魔法:

_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)
    // ...
}
_
3
dewey

パーセルヘルパークラスを使用して、オブジェクトをパーセルとの間で変換しています。

この要旨を参照してください: 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);
    }
}
0
Stéphane