ZdaR
ZdaR

Reputation: 22954

Instrumentation test cases for InputMethodService

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.

Upvotes: 5

Views: 286

Answers (1)

Hitchhiker
Hitchhiker

Reputation: 99

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

Upvotes: 0

Related Questions