web-dev-qa-db-fra.com

Tests unitaires Android: Bundle/Parcelable

Comment testez-vous Parcelable? J'ai créé une classe Parcelable et écrit ce test unitaire

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

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

J'essaie délibérément d'échouer au test en retournant null dans createFromParcel(), mais il semble réussir. Il semble que le colis ne soit pas fragmenté tant que ce n’est pas nécessaire. Comment puis-je forcer le Bundle à ... grouper?

29
garbagecollector

J'ai trouvé ce lien qui montre comment tester à la pièce un objet pouvant être parcellisé: http://stuffikeepforgettinghowtodo.blogspot.nl/2009/02/unit-test-your-custom-parcelable.html

Vous pouvez réellement sauter la Bundle si vous n'avez pas vraiment besoin de l'inclure, comme l'a suggéré zorch. Vous obtiendrez alors quelque chose comme ceci:

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

Vous pouvez le faire de cette manière:

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

Comme cette question et les réponses m'ont aidé plusieurs années sur toute la ligne, j'ai pensé ajouter ma propre suggestion, à savoir assertque la dataPosition() à la fin de la lecture était la même qu'à la fin de l'écriture. S'appuyant sur La réponse de 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());
}

Arrière-plan: Cela m'est venu après avoir passé un temps (embarrassant) à déboguer un mauvais Parcelablename__. Dans mon cas, writeToParcelétait en train d'écrire un champ en double à partir d'un objet dans un graphe d'objet moyennement complexe. Par conséquent, les objets subséquents ont été lus de manière incorrecte, avec des exceptions trompeuses et aucune erreur lors des tests avec l’objet spécifique se comportant mal.

J'ai eu du mal à retrouver la trace, puis j'ai réalisé que vérifier dataPositionaurait permis de localiser le problème plus rapidement, car j'ai des tests sur les objets internes ainsi que sur le conteneur principal.


Kotlin: Depuis que je travaille à Kotlin, un peu de magie lambda et réifiante:

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
}

Je peux maintenant tester très facilement dans ce sens, s’il existe une equals() complète et fiable:

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

Notez que le .parcel.test évite de devoir spécifier à nouveau le paramètre de type générique en utilisant cette réponse .

Ou, pour des assertions plus complexes:

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

J'utilise une classe d'assistance de parcelle pour convertir un objet en/à partir de parcelle.

Voir cet article: https://Gist.github.com/scallacs/f749a7385bcf75829a98d7b651efd02e

L'utilisation est très simple:

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

La mise en oeuvre:

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