Skip to content
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

mockStatic PendingIntent got NPE #168

Closed
paulhu0819 opened this issue Aug 11, 2020 · 1 comment · Fixed by #170
Closed

mockStatic PendingIntent got NPE #168

paulhu0819 opened this issue Aug 11, 2020 · 1 comment · Fixed by #170
Assignees

Comments

@paulhu0819
Copy link

I tried to use mockStatic to mock PendingIntent class in my test but it got NPE immediately after called startMonitoring().

Code:

@Before
fun setUp() {
     session = mockitoSession()
             .initMocks(this)
             .mockStatic(PendingIntent::class.java)
             .strictness(Strictness.LENIENT)
             .startMocking()
}

Logs:

06-10 16:00:53.355 27681 27703 E TestRunner: java.lang.NullPointerException: Attempt to invoke interface method 'android.os.IBinder android.content.IIntentSender.asBinder()' on a null object reference
06-10 16:00:53.355 27681 27703 E TestRunner: 	at android.app.PendingIntent.hashCode(PendingIntent.java:1192)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at java.util.HashMap.hash(HashMap.java:338)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at java.util.HashMap.put(HashMap.java:611)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.InlineStaticMockMaker.createMock(InlineStaticMockMaker.java:167)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.MockMakerMultiplexer.createMock(MockMakerMultiplexer.java:65)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:69)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at org.mockito.Mockito.mock(Mockito.java:1905)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at org.mockito.Mockito.mock(Mockito.java:1814)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder.lambda$mockStatic$0(StaticMockitoSessionBuilder.java:58)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.extended.-$$Lambda$StaticMockitoSessionBuilder$AoyQNqvLdWW5bc2GJ4H8RWXvBuo.get(Unknown Source:2)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.extended.StaticMockitoSession.mockStatic(StaticMockitoSession.java:98)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder.startMocking(StaticMockitoSessionBuilder.java:144)
06-10 16:00:53.355 27681 27703 E TestRunner: 	at com.android.networkstack.tethering.TetheringNotificationUpdaterTest.setUp(TetheringNotificationUpdaterTest.kt:148)

I have looked into the NPE, the problem is that InlineStaticMockMaker uses HashMap to store mock object data that will call to real PendingIntent.hascode() when trying to put mock PendingIntent object as a HashMap key.
To solve the NPE, I suggest InlineStaticMockMaker may use MockMap like InlineDexmakerMockMaker to store mock object data safely.

dexmaker-mockito-inline-extended/src/main/java/com/android/dx/mockito/inline/InlineStaticMockMaker.java

    /**
     * All currently active mock markers. We modify the class's byte code. Some objects of the class
     * are modified, some are not. This list helps the {@link MockMethodAdvice} help figure out if a
     * object's method calls should be intercepted.
     */
    private final HashMap<Object, InvocationHandlerAdapter> markerToHandler = new HashMap<>();
    private final Map<Class, Object> classToMarker = new HashMap<>(); /***** Should use MockMap instead. *****/

....

    @Override
    public <T> T createMock(MockCreationSettings<T> settings, MockHandler handler) {
        Class<T> typeToMock = settings.getTypeToMock();
        if (!typeToMock.equals(mockingInProgressClass.get()) || Modifier.isAbstract(typeToMock
                .getModifiers())) {
            return null;
        }

        Set<Class<?>> interfacesSet = settings.getExtraInterfaces();
        InvocationHandlerAdapter handlerAdapter = new InvocationHandlerAdapter(handler);

        classTransformer.mockClass(MockFeatures.withMockFeatures(typeToMock, interfacesSet));

        Instantiator instantiator = Mockito.framework().getPlugins().getDefaultPlugin
                (InstantiatorProvider2.class).getInstantiator(settings);

        T mock;
        try {
            mock = instantiator.newInstance(typeToMock);
        } catch (org.mockito.creation.instance.InstantiationException e) {
            throw new MockitoException("Unable to create mock instance of type '" + typeToMock
                    .getSimpleName() + "'", e);
        }

        if (classToMarker.containsKey(typeToMock)) {
            throw new MockitoException(typeToMock + " is already mocked");
        }
        classToMarker.put(typeToMock, mock); /***** Got NPE here *****/

        markerToHandler.put(mock, handlerAdapter);
        return mock;
    }
@chao2zhang chao2zhang self-assigned this Aug 25, 2020
@chao2zhang
Copy link
Contributor

Hey @paulhu0819 , thanks for reporting this bug. I have successfully reproduced it and going to work on your fix considering your suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants