Last active
January 28, 2021 06:26
-
-
Save dpmedeiros/7f7724fdf13fc5390bb05958448cdcad to your computer and use it in GitHub Desktop.
Mock main thread handler for use in Android unit tests (requires Mockito and PowerMock)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.dpmedeiros.androidtestsupportlibrary; | |
import android.os.Handler; | |
import android.os.Looper; | |
import org.mockito.invocation.InvocationOnMock; | |
import org.mockito.stubbing.Answer; | |
import org.powermock.api.mockito.PowerMockito; | |
import java.util.concurrent.Executors; | |
import java.util.concurrent.ScheduledExecutorService; | |
import java.util.concurrent.TimeUnit; | |
import static org.mockito.Mockito.*; | |
/** | |
* Utility methods that unit tests can use to do common android library mocking that might be needed. | |
*/ | |
public class AndroidMockUtil { | |
private AndroidMockUtil() {} | |
/** | |
* Mocks main thread handler post() and postDelayed() for use in Android unit tests | |
* | |
* To use this: | |
* <ol> | |
* <li>Call this method in an {@literal @}Before method of your test.</li> | |
* <li>Place Looper.class in the {@literal @}PrepareForTest annotation before your test class.</li> | |
* <li>any class under test that needs to call {@code new Handler(Looper.getMainLooper())} should be placed | |
* in the {@literal @}PrepareForTest annotation as well.</li> | |
* </ol> | |
* | |
* @throws Exception | |
*/ | |
public static void mockMainThreadHandler() throws Exception { | |
PowerMockito.mockStatic(Looper.class); | |
Looper mockMainThreadLooper = mock(Looper.class); | |
when(Looper.getMainLooper()).thenReturn(mockMainThreadLooper); | |
Handler mockMainThreadHandler = mock(Handler.class); | |
Answer<Boolean> handlerPostAnswer = new Answer<Boolean>() { | |
@Override | |
public Boolean answer(InvocationOnMock invocation) throws Throwable { | |
Runnable runnable = invocation.getArgumentAt(0, Runnable.class); | |
Long delay = 0L; | |
if (invocation.getArguments().length > 1) { | |
delay = invocation.getArgumentAt(1, Long.class); | |
} | |
if (runnable != null) { | |
mainThread.schedule(runnable, delay, TimeUnit.MILLISECONDS); | |
} | |
return true; | |
} | |
}; | |
doAnswer(handlerPostAnswer).when(mockMainThreadHandler).post(any(Runnable.class)); | |
doAnswer(handlerPostAnswer).when(mockMainThreadHandler).postDelayed(any(Runnable.class), anyLong()); | |
PowerMockito.whenNew(Handler.class).withArguments(mockMainThreadLooper).thenReturn(mockMainThreadHandler); | |
} | |
private final static ScheduledExecutorService mainThread = Executors.newSingleThreadScheduledExecutor(); | |
} |
Thanks, it helped me a lot! As a sidenote, with Mockito v. 2.4.0 there is no getArgumentAt(int, class)
method but just generic getArgument(int)
Cannot execute because of mockito 2 is incompatible with PowerMock, so PowerMockito.mockStatic(Looper.class);
aborted with error java.lang.NoSuchMethodError: org.mockito.internal.handler.MockHandlerFactory.createMockHandler(Lorg/mockito/mock/MockCreationSettings;)Lorg/mockito/internal/InternalMockHandler;
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how to @PrepareForTest protected class ?