New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mockito @InjectMocks broken #2442

Closed
floating-cat opened this Issue May 5, 2016 · 4 comments

Comments

Projects
None yet
3 participants
@floating-cat

floating-cat commented May 5, 2016

Description

java.lang.ClassCastException: Cannot cast android.widget.TextView$MockitoMock$809592802 to org.robolectric.shadows.ShadowActivity

    at java.lang.invoke.MethodHandleImpl.newClassCastException(MethodHandleImpl.java:361)
    at java.lang.invoke.MethodHandleImpl.castReference(MethodHandleImpl.java:356)
    at android.app.Activity.finish(Activity.java)
    at com.example.SimpleTest.test(SimpleTest.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:531)

Steps to Reproduce

@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
public class SimpleTest {
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    TextView textView;

    @InjectMocks
    Activity activity = Robolectric.setupActivity(FragmentActivity.class);

    @Test
    public void test() {
        activity.finish();
    }
}

This snippet doesn't work and always throw ClassCastException. If I remove @Mock annotation or remove activity.finish() or change FragmentActivity.class to Activity.class, this snippet works.

Robolectric & Android Version

Robolectric 3.1-rc1.
compileSdkVersion 23.

I also test this code in Robolectric 3.0, it throws java.lang.IllegalArgumentException: attempted to invoke public void org.robolectric.shadows.ShadowActivity.finish() on instance of class android.widget.TextView$MockitoMock$619060240, but TextView$MockitoMock$619060240 doesn't extend ShadowActivity.

I struggle this problem all day and still have no idea><, If you want other information, I am also glad to provide.

Thanks.

@jongerrish

This comment has been minimized.

Show comment
Hide comment
@jongerrish

jongerrish May 5, 2016

Contributor

Just guessing but I wonder if the JUnit rule has applied @InjectMocks to the ShadowActivity that has been attached by Robolectric though byte code injection to the real Activity?

Contributor

jongerrish commented May 5, 2016

Just guessing but I wonder if the JUnit rule has applied @InjectMocks to the ShadowActivity that has been attached by Robolectric though byte code injection to the real Activity?

@floating-cat

This comment has been minimized.

Show comment
Hide comment
@floating-cat

floating-cat May 5, 2016

@InjectMocks can inject dependencies into the real Activity. But I don't know whether

JUnit rule has applied @InjectMocks to the ShadowActivity that has been attached by Robolectric though byte code injection to the real Activity.

I also found that I can invoke some methods in Activity like Activity#onOptionsItemSelected(Menu) and Activity#getIntent(), but I can't invoke some methods which have a implementation in ShadowActivity like Activity#finish() and Activity#isFinishing().

floating-cat commented May 5, 2016

@InjectMocks can inject dependencies into the real Activity. But I don't know whether

JUnit rule has applied @InjectMocks to the ShadowActivity that has been attached by Robolectric though byte code injection to the real Activity.

I also found that I can invoke some methods in Activity like Activity#onOptionsItemSelected(Menu) and Activity#getIntent(), but I can't invoke some methods which have a implementation in ShadowActivity like Activity#finish() and Activity#isFinishing().

@floating-cat floating-cat changed the title from `@Mock` (from Mockito) doesn't work well with Robolectric to Mockito doesn't work well with Robolectric May 6, 2016

@jongerrish

This comment has been minimized.

Show comment
Hide comment
@jongerrish

jongerrish May 20, 2016

Contributor

Just a side note, but I highly recommend against mocking Android framework classes, especially views and contexts. This is because the internal workings of Android can change across versions and upgrading APIs may require changes in stubbing behaviour. In your example using a real TextView would be better. The one exception I would make is for simple listener/callback classes that you want to verify got called.

Contributor

jongerrish commented May 20, 2016

Just a side note, but I highly recommend against mocking Android framework classes, especially views and contexts. This is because the internal workings of Android can change across versions and upgrading APIs may require changes in stubbing behaviour. In your example using a real TextView would be better. The one exception I would make is for simple listener/callback classes that you want to verify got called.

@floating-cat

This comment has been minimized.

Show comment
Hide comment
@floating-cat

floating-cat May 20, 2016

Thanks for your advice. Actually I mocked a class written by myself. But when I wrote that test example to show this issue, I mocked the TextView because TextView is so common in Android and I wrote that code without thinking too much.
I must admit I wrote a bad test example, and now I learn from it. I will write a better test example if I have more chances somewhere.
You are right, I should not mock it. I recall I read a testing book tells my not to mock some dependencies from others and mock something you write.

Thank your very much.

edit: fix some typos.

floating-cat commented May 20, 2016

Thanks for your advice. Actually I mocked a class written by myself. But when I wrote that test example to show this issue, I mocked the TextView because TextView is so common in Android and I wrote that code without thinking too much.
I must admit I wrote a bad test example, and now I learn from it. I will write a better test example if I have more chances somewhere.
You are right, I should not mock it. I recall I read a testing book tells my not to mock some dependencies from others and mock something you write.

Thank your very much.

edit: fix some typos.

@xian xian closed this Jan 5, 2017

@xian xian reopened this Jan 5, 2017

@xian xian changed the title from Mockito doesn't work well with Robolectric to Mockito @InjectMocks broken Jan 5, 2017

@xian xian referenced this issue Jan 5, 2017

Merged

Fix @InjectMocks. #2820

@xian xian closed this in #2820 Jan 6, 2017

@xian xian added this to the 3.2.1 milestone Jan 6, 2017

@xian xian added the defect label Jan 6, 2017

@xian xian self-assigned this Jan 6, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment