12

Próbuję zintegrować Espresso 2.0's AndoridJUnitRunner z ActivityUnitTestCase. Jednak moje testy ulegają awarii, gdy startActivity() tries to initialize mMockParent = new MockParent().ActivityUnitTestCase rzuca wyjątek RuntimeException po uruchomieniu z AndroidJUnitRunner

Oto co zrobiłem:

Utwórz nowy projekt z Intellij 14 CE i wprowadzić pewne zmiany do 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') 
} 

Napisz test jednostki 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()); 

    } 
} 

Uruchom test, uzyskując ślad stosu.

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) 

ten ślad stosu jest rzeczywiście rethrow z

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

który występuje, gdy startActivity() tries to initialize 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; 

jestem brakuje czegoś innego, aby dostać AndroidJUnitRunner pracy z ActivityUnitTestCase? Każda pomoc będzie bardzo ceniona.

Odpowiedz

20

Po uderzeniu głową o ścianę, okna i różne meble, w końcu dostałem testy!

Od a response on its issue tracker dowiedziałem się, że nie można tworzyć nowych uchwytów z gwintu oprzyrządowania. Co oznacza, że ​​funkcja startActivity() musi zostać wywołana w wątku interfejsu użytkownika. Co prowadzi do 3 rozwiązań. runOnMainSync()

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

2.Create własną klasą bazową

1.Wykorzystanie Instrumentation który rozciąga ActivityUnitTestCase i zastąpić 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.Jeżeli zadzwonić startActivity() w metodzie testowej i cały test można uruchomić na głównym wątku, możesz po prostu opisać swoją metodę testową za pomocą @UiThreadTest.

Podczas testów wypróbowałem wszystkie 3 rozwiązania, , ale tylko 1 & 2 będzie działać.

+0

Nie testowałem tego, ale może 3 nie działa, ponieważ @UiThreadTest wydaje się mieć zastosowanie tylko do testów rozszerzających 'InstrumentationTestCase', a nie' ActivityUnitTestCase'? [Odniesienie do systemu Android] (http://developer.android.com/reference/android/test/UiThreadTest.html) – twelve17

+0

Przy okazji, dziękuję za pośrednie udzielenie odpowiedzi na pytanie, które miałem, dlatego musiałem zawracać sobie głowę z runOnMainSync na wszystko w mojej ActivityUnitTestCase, i odpowiedź jest taka, że ​​w końcu dodałem inne testy, które podklasy "ActivityInstrumentationTestCase2", i zainstalowałem Espresso, i to wymagało przełączenia 'testInstrumentationRunner' w moim pliku' build.gradle' z 'android.test.InstrumentationTestRunner' do 'android.support.test.runner.AndroidJUnitRunner', co zmienia wcześniejsze zachowanie możliwości bezpośredniego wywoływania metod związanych z interfejsem użytkownika. – twelve17

Powiązane problemy