passsy
passsy

Reputation: 5222

Test Activity onCreate Exception

I have the following Activity that throws an exception if something is configured wrong.

public class MyActivity extends AppCompatActivity {

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

I tried to write a test for this ActivityInstrumentationTestCase2

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

which throws the correct Exception but doesn't run in my catch block due to some internal Sandboxing of the ActivityInstrumentationTestCase2.

So I tried it with plain 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");
    }
}

which does not work

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)

Any idea how to test this simple case?

Update #1

I tried absolutely everything. I reduced it to the absolute minimum which doesn't work.

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

Update #2

I started from the beginning. Generated a new project, threw the Exception

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

an tried to catch it with 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"));
        }

    }
}

I got this (complete) stacktrace which does not lead to my test. The system seems to call onCreate, perhaps from a different process, not my test.

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)

Upvotes: 11

Views: 3213

Answers (3)

Juan S&#225;nchez
Juan S&#225;nchez

Reputation: 990

getActivity is indeed calling onCreate method, so it is failing with your programmed exception and it throws another one (RuntimeException) with your generated one as the root cause.

Upvotes: 0

Tim van der Lippe
Tim van der Lippe

Reputation: 768

Why do you mock the class you are trying to test? You should mock dependencies of MyActivity to test that its methods are correctly using the mocks.

For example if you want to test class A which depends on B and C, then you want to create 2 mocks for B and C and a concrete object of A. Then you inject those 2 mocks in your object and you can start calling methods on it.

This is probably also the reason you get a java.lang.AbstractMethodError (there is not enough code posted to confirm it though). If you call a real method on a mock, whereas this method is abstract (for example you are mocking an interface or abstract class), then this error is thrown.

Below I posted some code and a test as example of how you can insert mocks into a concrete object.

In code:

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

with a test:

@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();
  }
}

Upvotes: 1

Ircover
Ircover

Reputation: 2446

You are throwing IllegalArgumentException and catching IllegalStateException. You can add another catch block with catching Exception - it will work.

Upvotes: 2

Related Questions