Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue with Robolectric with new version of Google Play services

I use Robolectric for unit tests, I have Google Play Services in my project. This worked fine, until yesterday, when Google Play Services updated to a new version. I get this error:

java.lang.NullPointerException
at com.google.android.gms.common.GooglePlayServicesUtil.zzh(Unknown Source)
at com.google.android.gms.common.GooglePlayServicesUtil.zzd(Unknown Source)
at com.google.android.gms.common.GoogleApiAvailability.isGooglePlayServicesAvailable(Unknown Source)
at com.google.android.gms.common.api.zzg$zze.zznn(Unknown Source)
at com.google.android.gms.common.api.zzg$zzi.run(Unknown Source)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 255

It seems the Shadow class is not called, GooglePlayServicesUtil is called giving the NullPointerException. Has anyone seen this?

I don't even use Google Play Services in the tests.

like image 378
Christine Avatar asked Aug 17 '15 13:08

Christine


1 Answers

I've added next workaround and it works fine:

  1. Extract all PlayServices' related code to Utility class (in my case it is just Availability check):

    public class PlayServicesUtils {
    
        private static final int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
    
        public static final int AVAILABLE = 1;
        public static final int ERROR_RESOLVABLE = 2;
        public static final int ERROR_UNRESOLVABLE = 3;
    
        @IntDef({AVAILABLE, ERROR_RESOLVABLE, ERROR_UNRESOLVABLE})
        @Retention(RetentionPolicy.SOURCE)
        public @interface PlayServicesAvailability {
        }
    
        @PlayServicesAvailability
        public static int checkPlayServices(@NonNull Activity activity) {
            GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
            int resultCode = apiAvailability.isGooglePlayServicesAvailable(activity);
            if (resultCode != ConnectionResult.SUCCESS) {
                if (apiAvailability.isUserResolvableError(resultCode)) {
                    apiAvailability.getErrorDialog(activity, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST).show();
                    return PlayServicesUtils.ERROR_RESOLVABLE;
                } else {
                    CLog.e(Constants.TAG, "This device does not support Google Play services.");
                    return PlayServicesUtils.ERROR_UNRESOLVABLE;
                }
            }
            return PlayServicesUtils.AVAILABLE;
        }
    }
    
  2. Implement shadow for this Utility class:

    @Implements(PlayServicesUtils.class)
    public class ShadowPlayServicesUtils {
    
        @Implementation
        @PlayServicesUtils.PlayServicesAvailability
        public static int checkPlayServices(@NonNull Activity activity) {
            return PlayServicesUtils.AVAILABLE;
        }
    }
    
  3. Add shadow to your test class (or to base level test class):

    @Ignore
    @RunWith(TestRunner.class)
    @Config(
            sdk = 18,
            constants = BuildConfig.class,
            shadows = {
                    ShadowPlayServicesUtils.class
            }
    )
    public abstract class BaseTest {
        // some code, maybe 
    }
    
  4. Add your shadow to TestRunner's InstrumentationConfiguration creation:

    public class TestRunner extends RobolectricGradleTestRunner {
        public TestRunner(Class<?> klass) throws InitializationError {
            super(klass);
        }
    
        @Override
        public InstrumentationConfiguration createClassLoaderConfig() {
            InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder();
    
            builder.addInstrumentedClass(PlayServicesUtils.class.getName());
    
            return builder.build();
        }
    }
    

Original answer:

I've found similar issue on Robolectric issue tracker and workaround which is provided there - works!

Just force success initialisation of Google Play Services:

@Before public void setUp() {
    // force success every time
    ShadowGooglePlayServicesUtil.setIsGooglePlayServicesAvailable(ConnectionResult.SUCCESS);
}

EDIT:

But there is another issue with Play Services 8.3 and 8.4. And this issue is still not fixed.

like image 59
Oleksandr Avatar answered Oct 24 '22 07:10

Oleksandr