unittest

Fear of fixing code

A while back I was working on a task of retrospectively documenting a project written a couple of years back. Whilst I was going through the code building up my understanding of it I came across a number of code smells. Nothing major fortunately but, things like, print errors to System.out rather than using loggers, code that appeared to no longer be required, things that should be refactored for clarity etc. Although this isn’t great, it’s no surprise and I’ve seen many projects like this in the past. Also, developers will have come across issues with my code written before I appreciated the benefits of automation.

alone-62253_1280

Fear

The most painful part with these issues was the fear of breaking the product if I addressed the issues.

I had this fear because the project had no automated tests to speak of which meant the only way of verifying whether things were still working would be to manually test the code myself which isn’t the best plan or schedule some time in with QA. Neither of these are an option because of cost, so because of this the code was left as is.

The trouble is, this will only get worse and over time the code will begin to rot. Changes will take longer, more manual testing is required etc. and all this costs time and money.

Solution

When writing any code whether it is new or adding to existing code, make sure that it has automated tests to prevent the rot and reduce the fear of making changes that can be verified using automation. You don’t have to use TDD, however, once you get the hang of it, it will certainly make writing tests a lot quicker. I recommend starting as soon as you can, to understand the ways in which to write unit and integration tests and you will soon find comfort that whenever you need to refactor code, add functionality etc. you will have the security blanket of your automated tests to keep you calm!

Conflict with Robolectric and SlideUpPanelLayout

The other day I was trying to integrate Robolectric with my project that uses the SlideUpPanelLayout. If I removed either Robolectric from the test build or SlideUpPanelLayout from my project it fixed the issue (temporarily, because I needed both) When using Robolectric and SlideUpPanelLayout together I get the following error.

Test failed to run to completion. Reason: ‘Instrumentation run failed due to 'java.lang.IllegalAccessError''. Check device logcat for details
Test running failed: Instrumentation run failed due to 'java.lang.IllegalAccessError'

This error is down to a Gradle configuration error. The reason is that there are dependencies declared multiple times in the build and one of them needs to be excluded. The challenge in this situation is knowing which dependency it is.

To work this out look at the exception which in this case ended with

Caused by: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
at com.sothree.slidinguppanel.ViewDragHelper.<init>(ViewDragHelper.java:392)
at com.sothree.slidinguppanel.ViewDragHelper.create(ViewDragHelper.java:348)
at com.sothree.slidinguppanel.ViewDragHelper.create(ViewDragHelper.java:361)
at com.sothree.slidinguppanel.SlidingUpPanelLayout.<init>(SlidingUpPanelLayout.java:334)
at com.sothree.slidinguppanel.SlidingUpPanelLayout.<init>(SlidingUpPanelLayout.java:263)
... 26 more

ViewDragHelper was the culprit so if in your IDE (Android Studio) open the search for class name (Command-O in OS X) and enter ViewDragHelper. You will see that there are multiple Jars that are returned. You will need to exclude the one that you don’t want.

In this situation we had the android-19 sdk, support-v4-19 and support-v4-20 and library-2.0.1 (the slideuppanellayout library) to solve this I needed to exclude support-v4.

So in the build gradle file for the project where I included robolectric I needed to add the following to the list of excludes.

exclude module: 'support-v4'

This solved the problem!

NullPointerException when using Robolectric and Google Play Services

Today whilst writing a unit test for my Android application I had issues using Robolectric in my application. After a lot of hunting around and narrowing down the problem I found that it is was because of the declaration of Google Play Services in my AndroidManifest.xml file.

The error I got was

java.lang.RuntimeException: java.lang.NullPointerException
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:240)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:177)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:355)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
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)
Caused by: java.lang.NullPointerException
at org.robolectric.AndroidManifest$MetaData.init(AndroidManifest.java:698)
at org.robolectric.AndroidManifest.initMetaData(AndroidManifest.java:364)
at org.robolectric.res.builder.RobolectricPackageManager.addManifest(RobolectricPackageManager.java:364)
at org.robolectric.internal.ParallelUniverse.setUpApplicationState(ParallelUniverse.java:77)
at org.robolectric.RobolectricTestRunner.setUpApplicationState(RobolectricTestRunner.java:430)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
... 35 more

The key parts of this stack trace being the NullPointerException and the fact it was coming from AndroidManifest.java. The cause of this issue was down to the fact in the AndroidManifest.xml I have this declaration to include Google Maps.


<meta-data android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />

The part that is causing issues is the @integer/google_play_services_version and it is something to do with the fact the google_play_services_version is embedded in the Jar and it cannot be processed at the point it needs to be.

How to fix this?
To fix this issue you will can hard code it but that is a very hacky solution. It would be better to define this value locally but still point it at the correct value, for some reason this works.

So, in the values section of your project create a new resource file and call it something like values.xml.

In that file add the following code


<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="gms_version">@integer/google_play_services_version</integer>
</resources>

Then in your AndroidManifest.xml file where @integer/google_play_services_version was previously declared then put @integer/gms_version

This will work around the issue which is open here.