11

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)
passsy
  • 5,162
  • 4
  • 39
  • 65
  • why `.onCreate(null);`? i never used tests but i'm sure that `getActivity()` should return "fully created" activity, there is no need to call `onCreate` – Selvin May 13 '15 at 14:18
  • You are correct. But either way, same problem – passsy May 18 '15 at 09:20
  • Did you try using your own uncaughtExceptionHandler to evaluate further? :http://www.intertech.com/Blog/android-handling-the-unexpected/ and/or did you have a look at https://forums.xamarin.com/discussion/13784/catching-global-exceptions-in-android – serv-inc May 21 '15 at 08:41
  • 2
    Check your stack-trace. You'll notice it doesn't call your test case. This is because the `Activity` is being created by the Instrumentation framework before it even runs any of your tests. So of course your try catch is never going to be caught. You cannot throw Exceptions from `onCreate` and expect them to work; since `startActivity` is asynchronous. Instead you should call `setResult()` and `finish()` your `Activity`. – Simon May 23 '15 at 10:03

3 Answers3

2

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

Ircover
  • 2,406
  • 2
  • 22
  • 42
  • Good catch! but it was only a typo when creating the snippet for SO. I double checked it in the code and I'm catching the correct Exception – passsy May 18 '15 at 08:56
  • @passsy And what is wrong now? You are throwing exception in `MyActivity.onCreate` and it is successfully catched. – Ircover May 18 '15 at 09:59
  • I throw in MyActivity#onCreate() and I don't catch it because getActivity() is sandboxed by the InstrumentationTestCase2. See Update #1 for the stacktrace – passsy May 18 '15 at 11:29
1

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();
  }
}
  • I just want to check if the Intent extras contains all required fields. Mockito was only one experiment to make sure the onCreate Method is called outside of the sandbox which prevents me from catching the exception. – passsy May 18 '15 at 09:24
0

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.

Juan Sánchez
  • 980
  • 2
  • 10
  • 26
  • does not work . My call from my test doesn't appear in the stacktrace and the catch block is never called. See Update #2 – passsy May 25 '15 at 09:41
  • It does apper. Check the line starting with "Caused by". There you can see your generated exception message. – Juan Sánchez May 25 '15 at 10:57
  • Yes this is where exception is thrown. But there is no line from my test where I'm calling this method. Something like: `at com.pascalwelsch.onccreatetest.MainActivityTest.testOnCreate(MainActivityTest.java:16)`. – passsy May 25 '15 at 16:29