Instrumentation test cases for InputMethodService - android

I have extended the InputMethodService class to create my a custom IME. However, I am struggling to write valid Instrumentation test cases to verify the behavior. Previously the Service, could be tested using ServiceTestCase<YourServiceClass>. But it seems to have been deprecated, and the new format looks like this. Now in the given guidelines, I am struggling with this snippet:
CustomKeyboardService service =
((CustomKeyboardService.LocalBinder) binder).getService();
Since I am extending InputMethodService, it has already abstracted the IBinder, how can I obtain LocalBinder to get this snippet running? Currently, this snippet throws following exception:
java.lang.ClassCastException:
android.inputmethodservice.IInputMethodWrapper cannot be cast to
com.osrc.zdar.customkeyboard.CustomKeyboardService$LocalBinder
The extended class looks like:
public class CustomKeyboardService extends InputMethodService {
// Some keyboard related stuff
public class LocalBinder extends Binder {
public CustomKeyboardService getService() {
// Return this instance of LocalService so clients can call public methods.
return CustomKeyboardService.this;
}
}
// Some keyboard related stuff
}
How can I extend my custom class such that CustomKeyboardService service
= ((CustomKeyboardService.LocalBinder) binder).getService(); doesn't returns error?
Here is my test case code:
#RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest2 {
#Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
#Test
public void testWithBoundService() throws TimeoutException {
// Create the service Intent.
Intent serviceIntent =
new Intent(InstrumentationRegistry.getTargetContext(), CustomKeyboardService.class);
// Bind the service and grab a reference to the binder.
IBinder binder = mServiceRule.bindService(serviceIntent);
// Get the reference to the service, or you can call public methods on the binder directly.
// This Line throws the error
CustomKeyboardService service =
((CustomKeyboardService.LocalBinder) binder).getService();
}
}
You can also check out OimeKeyboard on Github for full source code and submit a PR with a valid instrumentation test case.

Same issue happend to me, please check below link's solution.
Updated code snippets from link :
#Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
private MyKeyboard retrieveMyKeyboardInstance(IBinder binder) {
try {
Class wrapperClass = Class.forName("android.inputmethodservice.IInputMethodWrapper");
Field mTargetField = wrapperClass.getDeclaredField("mTarget");
mTargetField.setAccessible(true);
WeakReference<MyKeyboard> weakReference = (WeakReference<MyKeyboard>) mTargetField.get(binder);
return weakReference.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void validateEditTextWithKeyboardInput() throws TimeoutException {
...
Intent serviceIntent = new Intent(InstrumentationRegistry.getTargetContext(), MyKeyboard.class);
IBinder binder = mServiceRule.bindService(serviceIntent);
MyKeyboard keyboard = retrieveMyKeyboardInstance(binder);
...
}
From : https://github.com/sealor/prototype-Android-Espresso-Keyboard-Testing

Related

PowerMock argument captor returns null for new Intent

Here is my main class and the method that I'm trying to test
public class MyClass {
public void startEmailActivity(FragmentActivity activity, #NotNull String emailUrl) {
if (isMyEmailAppInstalled()) {
Intent myEmailAppIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(emailUrl));
myEmailAppIntent.setClassName(MY_PACKAGE_NAME, MY_EMAIL_COMPOSE_ACTIVITY_EMAIL);
activity.startActivity(intent);
}
}
And here is the test class and method. Assume that I've mocked necessary calls inside isMyEmailAppInstalled() method such that it returns true
#RunWith(PowerMockRunner.class)
#PrepareForTest(Uri.class)
public class MyClassTest {
#Mock
FragmentActivity mockActivity;
#Mock
private Uri mockUri;
#Captor
private ArgumentCaptor<Intent> intentArgumentCaptor;
private static final String MOCK_EMAIL_URL = "mailto:mock#mock.com";
#Test
public void testStartEmailActivity() throws Exception {
doNothing().when(mockActivity).startActivity(any(Intent.class));
mockStatic(Uri.class);
when(Uri.parse(MOCK_EMAIL_URL)).thenReturn(mockUri);
MyClass testObject = new MyClass();
testObject.startEmailActivity(mockActivity, MOCK_EMAIL_URL);
intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockActivity).startActivity(intentArgumentCaptor.capture());
Intent sentIntent = intentArgumentCaptor.getValue();
//sentIntent is null here :( Below lines of code throws NPE when test is run
Assert.assertTrue(sentIntent.getComponent().getClassName().equalsIgnoreCase(MY_EMAIL_COMPOSE_ACTIVITY_EMAIL));
Assert.assertTrue(sentIntent.getComponent().getPackageName().equalsIgnoreCase(MY_PACKAGE_NAME));
}
}
Anyone have an idea why the argument captor would return null? It seems like simple thing, may be I'm missing something.
Aha! I found out the missing piece.
Basically I needed two things.
add the preparefortest for the class under test which will allow powermock to get inside this and help with mocking i.e
#PrepareForTest({Uri.class, MyClass.class})
mock the construction of new object using whenNew()
whenNew(Intent.class).withAnyArguments().thenReturn(mockIntent);
So this is what my final test method looks like
#Test
public void testStartEmailActivity() throws Exception {
doNothing().when(mockActivity).startActivity(any(Intent.class));
mockStatic(Uri.class);
when(Uri.parse(MOCK_EMAIL_URL)).thenReturn(mockUri);
when(mockIntent.setClassName(MY_PACKAGE_NAME, MY_EMAIL_COMPOSE_ACTIVITY_EMAIL)).thenReturn(mockIntent);
whenNew(Intent.class).withAnyArguments().thenReturn(mockIntent);
MyClass testObject = new MyClass();
testObject.startEmailActivity(mockActivity, MOCK_EMAIL_URL);
verify(mockActivity).startActivity(mockIntent);
}

Android: get reference to started Service in instrumentation test

I'm trying to write instrumentation test for my NetworkMonitorService as described in the official "testing your service" documentation.
Currently I'm stuck because I can't figure out how can I grab a reference to the started service in order to inject mocks into it and assert behavior.
My code:
#RunWith(AndroidJUnit4.class)
#SmallTest
public class NetworkMonitorServiceTest {
#Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
#Test
public void serviceStarted_someEventHappenedInOnStartCommand() {
try {
mServiceTestRule.startService(new Intent(
InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class));
} catch (TimeoutException e) {
throw new RuntimeException("timed out");
}
// I need a reference to the started service in order to assert that some event happened
// in onStartCommand()...
}
}
The service in question doesn't support binding. I think that if I'd implement support for binding and then use this in test in order to get a reference to the service it could work. However, I don't like writing production code just for sake of supporting test cases...
So, how can I test (instrumentation test) a Service that doesn't support binding?
Replace your application with special version "for tests". Do it by providing custom instrumentation test runner. Mock your dependencies it this "app for tests". See for details
Here is a simplified example how "app for test" can be used. Let's assume you want to mock network layer (eg. Api) during tests.
public class App extends Application {
public Api getApi() {
return realApi;
}
}
public class MySerice extends Service {
private Api api;
#Override public void onCreate() {
super.onCreate();
api = ((App) getApplication()).getApi();
}
}
public class TestApp extends App {
private Api mockApi;
#Override public Api getApi() {
return mockApi;
}
public void setMockApi(Api api) {
mockApi = api;
}
}
public class MyTest {
#Rule public final ServiceTestRule mServiceTestRule = new ServiceTestRule();
#Before public setUp() {
myMockApi = ... // init mock Api
((TestApp)InstrumentationRegistry.getTargetContext()).setMockApi(myMockApi);
}
#Test public test() {
//start service
//use mockApi for assertions
}
}
In the example dependency injection is done via application's method getApi. But you can use Dagger or any others approaches in the same way.
I found a very simple way for doing this. You can just perform a binding and you'll get the reference to the already running service, there are no conflicts with service creation because you already started it with onStartCommand, if you check you will see onCreate is called only once so you can be sure it is the same service. Just add the following after your sample:
Intent serviceIntent =
new Intent(InstrumentationRegistry.getTargetContext(),
NetworkMonitorService.class);
// Bind the service and grab a reference to the binder.
IBinder binder = mServiceRule.bindService(serviceIntent);
// Get the reference to the service
NetworkMonitorService service =
((NetworkMonitorService.LocalBinder) binder).getService();
// Verify that the service is working correctly however you need
assertThat(service, is(any(Object.class)));
I hope it helps.
this works at least for bound services:
#Test
public void testNetworkMonitorService() throws TimeoutException {
Intent intent = new Intent(InstrumentationRegistry.getTargetContext(), NetworkMonitorService.class);
mServiceRule.startService(intent);
IBinder binder = mServiceRule.bindService(intent);
NetworkMonitorService service = ((NetworkMonitorService.LocalBinder) binder).getService();
mServiceRule.unbindService();
}
to access fields, annotate with #VisibleForTesting(otherwise = VisibleForTesting.NONE)

How to unit test methods posted by Event Bus in android?

I am using Otto's event bus in my application. In one of my classes I am posting the event.
MyEvent myevent = new MyEvent();
uiBus.post(myEvent);
I am able to test the post method.
Now there is another class which is receiving the event.
//ReceiverClass.java
#Subscribe
public void onEventReceived(MyEvent myevent) {
callAMethod();
}
How do I unit test that this method was invoked. I tried with the following test code
#Mock
Bus uiBus;
#Test
public void testBusReceviedEvent() {
ReceiverClass instance = new ReceiverClass();
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myEvent);
//Test
verify(instance, times(1)).callAMethod();
}
But this code doesn't work.
I'm a little late to the party but here is an example of a class which works and accounts for async calls. Instead of Mocking EventBus we simply let it do it's thing and register it in the TestDriver class below.
The thing that makes this work is the CountDownLatch which, with the help of the abstract DataTransferCallback class, waits for latch.countDown() to be called or 5 seconds to go by.
Just register your test class and in the #Subscribe method, pass it back to the method that created the DataTransferCallback and do your assertions there.
#RunWith(AndroidJUnit4.class)
public class TestDriver {
private final CountDownLatch latch = new CountDownLatch(1);
private EventBus eventBus;
private DataTransferCallback transferCallback;
public abstract class DataTransferCallback {
abstract void onSuccess(DataTransfer event);
}
#Before
public void setUp() {
EventBus.getDefault().register(this);
eventBus = spy(EventBus.getDefault());
}
#SuppressWarnings("unchecked")
#Test
public void test200Resposne() throws InterruptedException {
// Get known good JSON
final String json = TestJSON.get200Response();
// Class under test
final Driver driver = new Driver(InstrumentationRegistry.getTargetContext());
final JsonParser jsonParser = new JsonParser();
//boolean to hold our test result
final boolean[] testPassed = new boolean[]{false};
transferCallback = new DataTransferCallback() {
#Override
public void onSuccess(DataTransfer event) {
assertNotNull(event);
verify(eventBus).post(event);
assertThat(event.getStatus(), is("OK"));
assertTrue(event.getData() != null);
testPassed[0] = true;
}
};
//Set our test EventBus object
driver.setEventBus(eventBus);
// The actual method under test
driver.parseData(jsonParser.parse(json));
// Set a countdown latch to wait for the result (5s)
latch.await(5000, TimeUnit.MILLISECONDS);
// will wait here until 5s or the #Subscrube method is hit
assertTrue(testPassed[0]);
}
//Because we want to examine EventBus Output, register it
//to this class and pass the event back through our custom abstract class
#Subscribe
public void onReceiveEventBusEvent(DataTransfer event) {
assertNotNull(transferCallback);
transferCallback.onSuccess(event);
//notify latch so that we can proceed
latch.countDown();
}
}
It does not work because instance is not a mock. You will have to verify the effects of callAMethod or put that method in another class and inject a mock of this new class into your ReceiverClass class.
For example...
private class ReceiverClass {
private MyNewClass theNewClassIWasTalkingAbout;
// Stick in a setter for that ^
#Subscribe
public void onEventReceived(MyEvent myevent) {
theNewClassIWasTalkingAbout.callAMethod();
}
}
Then your test will have to change slightly...
#Mock
private MyNewClass mockNewClass;
#InjectMocks // This will be the "solid" implementation of the thing you are trying to test, it is not a mock...
private ReceiverClass instance;
#Test
public void testBusReceivedEvent() {
mockBus.register(instance);
MyEvent myevent = new MyEvent();
mockBus.post(myevent);
verify(mockNewClass, times(1)).callAMethod();
}
Hope this helps.

How to properly spy on an Activity

Mockito creates a proxy instance when some thing is spied on. Now, is there any way to forward setters that are then executed on that proxy instance to the real instance that sits behind it?
Rationale: I have an object instance that I do not have completely under my control, i.e. an Android activity. I can give most parts of my app the proxied version and that runs fine as is, but because I need to create the spy / proxy very early during the creation phase of the activity, it is not yet fully instantiated, e.g. the base context is not attached. This happens on the proxy instance and is of course not used by the activity instance itself (which refers to itself via Activity.this). The end result is that this leads to all kinds of crashes because resource resolving happens via this base context, so the internal Fragment machinery throws NPEs and more.
Here is some code:
public class CustomAndroidJUnitRunner extends AndroidJUnitRunner {
#Override
public Activity newActivity(ClassLoader cl, String className, Intent intent)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
Activity activity = super.newActivity(cl, className, intent);
return maybeStubSomeDelegate(activity);
}
private Activity maybeStubSomeDelegate(Activity activity) {
if (!(activity instanceof SomeDelegate)) {
return activity;
}
Activity spiedActivity = spy(activity);
doReturn(SomeDelegateMock.getInstance())
.when((SomeDelegate) spiedActivity)
.getDelegate();
return spiedActivity;
}
}
I'm clueless - any ideas?
Using:
android Test Support library's
SingleActivityFactory,
ActivityTestRule
and Mockito's
spy()
dependencies {
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'org.mockito:mockito-android:2.21.0'
}
Outline:
inject the spied instance inside SingleActivityFactory's implementation
Code:
public class MainActivityTest {
MainActivity subject;
SingleActivityFactory<MainActivity> activityFactory = new SingleActivityFactory<MainActivity>(MainActivity.class) {
#Override
protected MainActivity create(Intent intent) {
subject = spy(getActivityClassToIntercept());
return subject;
}
};
#Rule
public ActivityTestRule<MainActivity> testRule = new ActivityTestRule<>(activityFactory, true, true);
#Test
public void activity_isBeingSpied() {
verify(subject).setContentView(R.layout.activity_main);
}
}
You can use Robolectric to create your own proxy (or as Robolectric calls them "Shadows") to your activity,
When you create the proxy you can create your own setters that can trigger the real object methods,
How to create a shadow example:
#Implements(Bitmap.class)
public class MyShadowBitmap {
#RealObject private Bitmap realBitmap;
private int bitmapQuality = -1;
#Implementation
public boolean compress(Bitmap.CompressFormat format, int quality, OutputStream stream) {
bitmapQuality = quality;
return realBitmap.compress(format, quality, stream);
}
public int getQuality() {
return bitmapQuality;
}
}
}
when the #RealObject is your real instance,
To use this shadow using Robolectric test runner define a new test class as follows:
#RunWith(RobolectricTestRunner.class)
#Config(shadows = MyShadowBitmap.class)
public class MyTestClass {}
To pull the current shadow instance use the method:
shadowOf()
And in any case, here is s link to Robolectric:
http://robolectric.org/custom-shadows/

Bind to a service - call back when bind has been successfully - Android

I've come to a point where I don't know an elegant way to do this.
Let's say I've a Fragment, named FragmentA, and a Service named BackupService
On FragmentA I bound it to the BackupService using:
private ServiceConnection backupServiceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
backupBoundService = binder.getService();
isBound = true;
// How to let the fragment know this has happened?
// Use an eventBus? EventBus.getDefault().post(backupBoundService); ?
Log.d(TAG, "On Service Connected -> Yup!");
}
#Override
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
And:
Intent intent = new Intent(ApplicationContextProvider.getContext(), BackupsService.class);
ApplicationContextProvider.getContext().bindService(intent, backupServiceConnection, Context.BIND_AUTO_CREATE); // Using application context
Now I know the binding is an asynchronous task and here is where my question comes in.
I came up with the idea of using an EventBus but I don't find it elegant as the fragment would be posting the object (in this case backupBoundService), referencing the service, and at the same time would be listening/receiving the event from the bus, eg, would be the same fragment posting and receiving the event (posting to himself).
Is there an elegant way to get a reference for the running service when the fragment is bounded to it? I'm quite sure there's a pattern for this case but I've been googling and searching here to no luck so far.
Hi you can use eventbus or you can create simple call back methods using Interfcae for you requirement.
If you don't have idea to create call backs using interface then look this piece of code.
It is same like eventbus :)
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
public void setCallBack(MyCallback callback)
this.callback=callback;
}
/////////////////////////////
class Callback implements MyCallback {
....
Worker worker= new Worker()
Worker.setCallback(this);
.. .
void callbackCall() {
// callback code goes here
//onEvent this method will execute
}
}
Hope this will helpful.
Good luck :)

Categories

Resources