Тестовая активность onCreate Exception

У меня есть следующее действие, которое генерирует исключение, если что-то настроено неправильно.

public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        throw new IllegalStateException("something went wrong");
    }
}

Я попытался написать тест для этого ActivityInstrumentationTestCase2

public void testException() throws Exception {
    try {
        getActivity().onCreate(null);
        fail();
    } catch (IllegalStateException e) {
        assertThat(e.getMessage()).contains("something went wrong");
    }
}

который выдает правильный Exception, но не работает в моем блоке catch из-за некоторого внутреннего Sandboxing ActivityInstrumentationTestCase2.

Итак, я попробовал это с простой Java

public void testException() throws Exception {
    final MockNavigationDrawerActivity activity = Mockito.mock(MockNavigationDrawerActivity.class);
    Mockito.doCallRealMethod().when(activity).onCreate(null);
    try {
        activity.onCreate(null);
        fail();
    } catch (IllegalStateException e) {
        assertThat(e.getMessage()).contains("something went wrong");
    }
}

который не работает

java.lang.AbstractMethodError: abstract method "boolean org.mockito.internal.invocation.AbstractAwareMethod.isAbstract()"
at org.mockito.internal.invocation.InvocationImpl.callRealMethod(InvocationImpl.java:109)
at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:41)
at org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:34)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:91)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:38)
at com.google.dexmaker.mockito.InvocationHandlerAdapter.invoke(InvocationHandlerAdapter.java:49)
at MockNavigationDrawerActivity_Proxy.onCreate(MockNavigationDrawerActivity_Proxy.generated)

Любая идея, как протестировать этот простой случай?

Обновление # 1

Я пробовал абсолютно все. Я уменьшил его до абсолютного минимума, который не работает.

public void testUpdate1() throws Exception {
    try {
        getActivity();
        fail();
    } catch (Exception e) {
        assertThat(e.getMessage()).contains("something went wrong");
    }
}

StackTrace:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/com.example.MyActivity}: java.lang.IllegalStateException: something went wrong
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
        at android.app.ActivityThread.access$800(ActivityThread.java:144)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
        Caused by: java.lang.IllegalStateException: something went wrong
        at com.example.MyActivity.onCreate(MyActivity.java:28)
        at android.app.Activity.performCreate(Activity.java:5933)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
        at android.support.test.runner.MonitoringInstrumentation.callActivityOnCreate(MonitoringInstrumentation.java:346)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
        ... 10 more

Обновление # 2

Я начал с самого начала. Создал новый проект, выбросил Exception

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    throw new IllegalStateException("something");
}

a попытался поймать его с помощью Throwable.

public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {

    public MainActivityTest() {
        super(MainActivity.class);
    }

    public void testOnCreate() throws Exception {
        try {
            getActivity();
            fail();
        } catch (Throwable throwable) {
            assertTrue(throwable.getCause().getMessage().contains("something"));
        }

    }
}

Я получил эту (полную) стек, которая не приводит к моему тесту. Кажется, система вызывает onCreate, возможно, из другого процесса, а не моего теста.

Process: com.pascalwelsch.onccreatetest, PID: 3915    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.pascalwelsch.onccreatetest/com.pascalwelsch.onccreatetest.MainActivity}: java.lang.IllegalStateException: something
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
        at android.app.ActivityThread.access$800(ActivityThread.java:144)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5221)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
 Caused by: java.lang.IllegalStateException: something
        at com.pascalwelsch.onccreatetest.MainActivity.onCreate(MainActivity.java:15)
        at android.app.Activity.performCreate(Activity.java:5933)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1105)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2251)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
            at android.app.ActivityThread.access$800(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5221)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)

Ответ 1

Вы бросаете IllegalArgumentException и ловите IllegalStateException. Вы можете добавить еще один блок catch с catch Exception - он будет работать.

Ответ 2

Почему вы издеваетесь над классом, который вы пытаетесь проверить? Вы должны издеваться над зависимостями MyActivity, чтобы проверить правильность его методов с помощью mocks.

Например, если вы хотите протестировать класс A, который зависит от B и C, то вы хотите создать 2 mocks для B и C и конкретный объект A. Затем вы вводите эти 2 mocks в свой объект, и вы можете начать называть его методы.

Вероятно, это также причина, по которой вы получаете java.lang.AbstractMethodError (этого недостаточно, чтобы подтвердить его). Если вы вызываете реальный метод на макет, тогда как этот метод является абстрактным (например, вы издеваетесь над интерфейсом или абстрактным классом), тогда эта ошибка возникает.

Ниже я опубликовал некоторый код и тест в качестве примера того, как вы можете вставлять mocks в конкретный объект.

В коде:

class A {
  B b;
  C c;

  void doSomething() {
    b.aMethod();
    c.anotherMethod();
    throw new IllegalArgumentException("something went wrong");
  }
}
interface B {
  void aMethod();
}
abstract class C {
  void anotherMethod();
}

с тестом:

@RunWith(MockitoJUnitRunner.class)
class ATest {
  @Mock B b;
  @Mock C c;
  // The inject mocks will insert both b and c into a by using reflection
  @InjectMocks A a;

  @Test(expected=IllegalArgumentException.class)
  public void testSomething() {
    a.doSomething();
  }
}

Ответ 3

getActivity действительно вызывает метод onCreate, поэтому он терпит неудачу с вашим запрограммированным исключением и выдает другой (RuntimeException) сгенерированным в качестве основной причины.