Pertanyaan ActivityUnitTestCase melempar RuntimeException ketika dijalankan dengan AndroidJUnitRunner


Saya mencoba untuk berintegrasi AndoridJUnitRunner dari Espresso 2.0 dengan ActivityUnitTestCase. Namun, tes saya gagal saat itu startActivity () mencoba menginisialisasi mMockParent = new MockParent ().

Inilah yang saya lakukan:

Buat proyek baru dengan Intellij 14 CE dan buat beberapa perubahan pada build.gradle.

android{
    defaultConfig {
        applicationId "com.noob.testing"
        minSdkVersion 9
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:21.0.+'

    androidTestCompile('org.mockito:mockito-core:1.9.5')
    androidTestCompile('com.google.dexmaker:dexmaker:1.2')
    androidTestCompile('com.google.dexmaker:dexmaker-mockito:1.2')

    androidTestCompile('com.android.support.test.espresso:espresso-core:2.0')
    androidTestCompile('com.android.support.test:testing-support-lib:0.1')
}

Tulis tes unit bergaya JUnit4.

@RunWith(AndroidJUnit4.class)
public class MainActivityJUnit4Test extends ActivityUnitTestCase<MainActivity> {
    public MainActivityJUnit4Test() {
        super(MainActivity.class);
    }

    MainActivity activity;

    @Before
    public void setup() throws Exception {
        injectInstrumentation(InstrumentationRegistry.getInstrumentation());
        super.setUp();
        ContextThemeWrapper context = new ContextThemeWrapper(getInstrumentation().getTargetContext(), R.style.AppTheme);
        setActivityContext(context);
        activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
    }

    @Test
    public void baseCase() {
        TextView tv = (TextView) activity.findViewById(R.id.tv);
        Assert.assertEquals("Hello World", tv.getText());

    }
}

Jalankan tes, dapatkan jejak stack.

junit.framework.AssertionFailedError
at junit.framework.Assert.fail(Assert.java:48)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertNotNull(Assert.java:218)
at junit.framework.Assert.assertNotNull(Assert.java:211)
at android.test.ActivityUnitTestCase.startActivity(ActivityUnitTestCase.java:147)
at com.sdchang.testing.MainActivityJUnit4Test.setup(MainActivityJUnit4Test.java:32)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:24)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
at org.junit.runner.JUnitCore.run(JUnitCore.java:136)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:270)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)

Jejak stack ini sebenarnya adalah rethrow of

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

yang terjadi saat itu startActivity () mencoba menginisialisasi mMockParent = new MockParent ():

            ComponentName cn = new ComponentName(mActivityClass.getPackage().getName(), 
                    mActivityClass.getName());
            intent.setComponent(cn);
            ActivityInfo info = new ActivityInfo();
            CharSequence title = mActivityClass.getName();
            mMockParent = new MockParent();
            String id = null;

Apakah saya kehilangan hal lain untuk membuat AndroidJUnitRunner bekerja dengan ActivityUnitTestCase? Bantuan apa pun akan sangat dihargai.


12
2018-01-16 19:28


asal


Jawaban:


Setelah membenturkan kepala saya ke dinding, jendela, dan berbagai perabotan, akhirnya saya berhasil melakukan tes!

Dari tanggapan atas pelacak masalahnyaSaya belajar bahwa tidak ada Handlers baru yang dapat dibuat dari benang instrumentasi. Yang berarti startActivity () harus dipanggil di utas UI. Yang mengarah ke 3 solusi.

1. Gunakan Instrumentasi runOnMainSync()

getInstrumentation().runOnMainSync(new Runnable() {
    @Override
    public void run() {
        activity = startActivity(new Intent(Intent.ACTION_MAIN), null, null);
    }
});

2. Buat kelas dasar Anda sendiri yang memperluas ActivityUnitTestCase dan menimpa startActivity

@Override
protected T startActivity(final Intent intent, final Bundle savedInstanceState,
        final Object lastNonConfigurationInstance) {
    return startActivityOnMainThread(intent, savedInstanceState, lastNonConfigurationInstance);
}

private T startActivityOnMainThread(final Intent intent, final Bundle savedInstanceState,
        final Object lastNonConfigurationInstance) {
    final AtomicReference<T> activityRef = new AtomicReference<>();
    final Runnable activityRunnable = new Runnable() {
        @Override
        public void run() {
            activityRef.set(YourBaseActivityUnitTestCase.super.startActivity(
                    intent, savedInstanceState, lastNonConfigurationInstance));
        }
    };

    if (Looper.myLooper() != Looper.getMainLooper()) {
        getInstrumentation().runOnMainSync(activityRunnable);
    } else {
        activityRunnable.run();
    }

    return activityRef.get();
}

3.Jika Anda memanggil startActivity () dalam metode pengujian dan seluruh pengujian Anda dapat berjalan di utas utama, Anda cukup menganotasi metode pengujian Anda dengan @UiThreadTest.

Dalam pengujian saya, saya sudah mencoba semua 3 solusi, tetapi hanya 1 & 2 yang akan berfungsi.


21
2018-02-06 10:42