Модульное тестирование Android: Bundle/Parcelable

Как вы проводите модульное тестирование 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(), но, похоже, это удается. Похоже, он не будет отправлен до тех пор, пока он не понадобится. Как заставить Bundle ..bundle?


person garbagecollector    schedule 10.10.2012    source источник
comment
Если какой-либо из наших ответов является решением вашей проблемы, отметьте его как принятый ответ :)   -  person Xilconic    schedule 27.01.2013


Ответы (4)


Я нашел эту ссылку, показывающую, как вы можете выполнить модульное тестирование объекта, подлежащего разделению: 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);
}
person Xilconic    schedule 27.01.2013
comment
parcel.setDataPosition(0); был секрет. Спасибо - person scorpiodawg; 16.07.2015
comment
У меня тоже работает. Спасибо!. - person ernestkamara; 15.06.2016
comment
Не имеет возможности работать! Вы всегда получите BadParcelableException. - person ddmytrenko; 11.07.2016
comment
@ddmytrenko BadParcelableException возникает, когда сам объект разделен неправильно. например Когда ваши объекты для чтения и записи не в том же порядке - person Daiwik Daarun; 01.08.2016
comment
на самом деле это здесь: developer.android .com/training/testing/unit-testing/ - person Irfan Ul Haq; 20.02.2020

Вы можете сделать это следующим образом:

//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));
person chivorotkiv    schedule 22.11.2012
comment
Вы пробовали с таким утверждением: Assert.assertTrue(EqualsBuilder.reflectionEquals(q2, q)); ? Я спрашиваю, потому что, если у вас есть поле List<obj> = new ArrayList<obj> в вашей модели данных Question.java, код, который вы написали, не работает. Если у вас есть ArrayList<obj> = new ArrayList<obj>, это работает. Вы знаете, как это исправить? - person denis_lor; 24.08.2017

Поскольку этот вопрос и ответы помогли мне через несколько лет, я подумал, что добавлю свое собственное предложение, которое будет состоять в том, чтобы assert чтобы dataPosition() в конце чтения было таким же, как в конце записи. Опираясь на ответ 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());
}

Предыстория: это пришло ко мне после того, как я потратил (постыдное) количество времени на отладку плохого Parcelable. В моем случае writeToParcel записывал повторяющееся поле из одного объекта в умеренно сложном графе объектов. Поэтому последующие объекты считывались неправильно, приводя к вводящим в заблуждение исключениям, и не возникало ошибок в тестах с конкретным некорректно работающим объектом.

Это было мучительно, но потом я понял, что проверка 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)
    // ...
}
person dewey    schedule 10.01.2018

Я использую вспомогательный класс посылки для преобразования объекта в/из посылки.

См. суть: 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);
    }
}
person Stéphane    schedule 17.05.2018