Android BluetoothAdapter Mocking - android

I'm trying to mock test Bluetooth application but my first step of creating a mock object of BluetoothAdapter class is not working!!
I'm using powermockito with easy mock.
mBluetoothAdapter = (BluetoothAdapter)PowerMock.createMock(BluetoothAdapter.class);
this fails. with the following stack trace
java.lang.IllegalArgumentException: No visible constructors in class android.bluetooth.BluetoothAdapter
at org.easymock.internal.DefaultClassInstantiator.getConstructorToUse(DefaultClassInstantiator.java:94)
at org.easymock.internal.AndroidClassProxyFactory.createProxy(AndroidClassProxyFactory.java:48)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:114)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:88)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:79)
at org.powermock.api.easymock.PowerMock.doCreateMock(PowerMock.java:2212)
at org.powermock.api.easymock.PowerMock.doMock(PowerMock.java:2163)
at org.powermock.api.easymock.PowerMock.createMock(PowerMock.java:89)
at com.xxx.blesimplesample.test.MainActivityTest.setUp(MainActivityTest.java:59)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1710)
Any one has used any mocking framework for Bluetooth app mocking? Any suggestions will be v helpful

BluetoothAdapter in the Android framework is declared final, so at the time you asked this question, it couldn't be mocked, neither with Mockito nor using Robolectric.
However, Android unit testing has changed a lot since then. With recent versions of the tools, when you build unit tests the tools generate a patched android.jar with all the finals removed. This makes all Android classes available for mocking. Nowadays, if you want to mock any Bluetooth code, you can do so in the standard way. The code you've already tried will "just work" if you update to the latest tools. Alternatively, Robolectric has a ShadowBluetoothAdapter class built in now.

Related

Is Android Gradle Plugin test apk builder automatically multidexing my test apk? If so, why is this generating "NoSuchMethodError"

This all started with this error when running instrumentation tests:
java.lang.NoSuchMethodError: No static method closeQuietly(Ljava/net/ServerSocket;)V in class Lokhttp3/internal/Util; or its super classes (declaration of 'okhttp3.internal.Util' appears in /data/app/com.example-vKdPJoTLl49ntRbZfsRBqQ==/base.apk!classes2.dex)
at okhttp3.mockwebserver.MockWebServer$2.execute(MockWebServer.java:333)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
I went looking into the generated test.apk to see what was present/missing in dex files.
First thing I noticed is that the test apk has two .dex files. Why? I'm not using multidex (and IIRC multidex doesn't work on test apks anyways). I then added up the two "referenced method counts" and sure enough I'm over the 65k limit. So is AGP auto-multi-dexing my test apk?
Furthermore I see the "missing" method in the first dex.
Why is it listed as a "reference" not a "defined" method? It's not like there is an OkHttp Util class provided by the framework.
In the initial crash, it said it can't find method in classes2.dex. Why is it looking in classes2.dex? Why not look in both?
I'm keeping everything in my test apk (using proguard but keeping everything) (maybe this explains the +65k ref methods). So why is this getting stripped/screwed with in the first place.
UPDATE:
It turns out this method was needed in my app .apk (not the test .apk). When I updated my proguard rules for the app apk everything worked. I still don't know why this class is needed in-app?
java.lang.NoSuchMethodError:
No static method closeQuietly(java.net.ServerSocket) in class okhttp3.internal.Util;
method closeQuietly(java.net.ServerSocket) might not be in use.
to explicitly keep it:
-keep class okhttp3.internal.Util {
public static void closeQuietly(java.net.ServerSocket);
}
I don't know how I didn't know this but apparently multidex is enabled by default on API 21+
https://developer.android.com/studio/build/multidex#mdex-on-l
Therefore, if your minSdkVersion is 21 or higher multidex is enabled
by default, and you do not need the multidex support library.
So yeah, this is normal. What might be happening is that the class is just not in the first dex file.

SecurityProvider in Mockito Tests when run alongside Robolectric

We haven an Android project where we use MockitoTestRunner and RobolectricTestRunner for different kinds of tests.
I have written a set of unit test that are concerned with SSL, thus loading certs/keystores/truststores, etc. For this I used the MockitoJUnitRunner and added the Bouncycastle provider programmatically as such:
Security.insertProviderAt(new BouncyCastleProvider(), 1);
Now, these tests run perfectly fine when run on their own - e.g. when I directly run single methods from the test classes, or run these classes from the Project tree menu, they work just fine.
But when I run said tests along side ANY test which uses the RobolectricTestRunner (such as if I just run all the tests in my project together before committing), I get the following exception:
java.io.IOException: error constructing MAC:
java.lang.SecurityException: JCE cannot authenticate the provider BC
I'm baffled. How will the testrunner used in one test class affect the run of other classes, especially if we use a different test runner?
Additional info:
The exception only occurs once I actually try do do something with the BC provider (e.g. the first time the test tries to load a PKCS12 certificate) - the insertProviderAt(...) call itself seems to pass fine...
Also when I print out the list of providers for each test run, I see that Robolectric already has a BC provider in there, but is still failing when I try to use it.
Also if I don't add the BC provider, the tests still fail with the same error when run in a test suite alongside Robolectric tests. When run alone they fail with java.security.NoSuchProviderException: no such provider: BC, as we're specifying the provider explicitly.
Seems like Robolectric is using its own classloader (that favors its replacements on the Android API), which could be in conflicts with the regular classloader of Mockito.
So for using at the same time the Robolectric and mockito, you may do the following:
Make use of the Robolectric runner. Robolectric uses its own classloader that favors its replacements for the Android API, so it really does need to handle classloading on its own. There's no other way to use Robolecric.
Replace the #RunWith(MockitoJUnitRunner.class) with these alternative methods covering the behaviour of MockitoJUnitRunner:
#Before public void setUpMockito() {
MockitoAnnotations.initMocks(this);
}
#After public void tearDownMockito() {
Mockito.validateMockitoUsage();
}
Maybe it could be a workaround for using the Robolectric classloader and Mockito at the same time.

ServiceLoader.load is not finding the META-INF/services

So I want to build an extensible android application where developers can add 'CustomDevice' classes and the main program will run them automatically without editing existing code.
I've read about Service Provider interface and thought that would be a nice way to go about it.
So I tested it and created an interface called 'ICustomDevice' which custom device classes are expected to implement.
I've created a class called 'DummyDevice' that implements ICustomDevice.
Both DummyDevice and ICustomDevice are in the same package "CustomDevicePackage".
So in my main program I run the following.
ServiceLoader<ICustomDevice> loader = ServiceLoader.load(ICustomDevice.class);
Iterator<ICustomDevice> devices = loader.iterator();
System.out.println("Does it have devices? " + devices.hasNext());
It always returns false, which means it's not finding the 'DummyDevice'
In my eclipse project I created a folder at 'src' called META-INF and under it, a subfolder called 'services'.
'Services' has a file named 'CustomDevicePackage.ICustomDevice' with a line of content 'CustomDevicePackage.DummyDevice'.
Am I doing it right? Every example I see about SPI is about loading JARS.
I'm not loading a JAR, I'm trying to run a class in the same Project. Does this method only works for loading JARs? I want my program to support loading local subclasses and external JARs alike.
I am adding this as an answer but leaving the prior "answer" to provide extended code detail for this workaround. I am working on reporting the prior answer results as a bug to Google.
Because the Android implementation of java.util.ServiceLoader is broken (always populating internal java.security.AccessControlContext field with AccessController.getContext() even if System.getSecurityManager() == null), the workaround is to create your own ServiceLoader class by copying the code found at OpenJDK for Java 8 into your class, add specific imports required from java.util without using import java.util.*;, and call that ServiceLoader in your code (you will have to fully reference the ServiceLoader you created to over ambiguity).
This isn't elegant but it is a functional workaround that works! Also, you will need to use a ClassLoader in your ServiceLoader.load() call. That ClassLoader will either have to be YourClass.class.getClassLoader() or a child ClassLoader of the class' ClassLoader.
Though it's an old post, This may be still be of some help to others:
When I was running or debugging a project that contained a ServiceLoader Class, I had to put the META-INF/services folder into the src/ folder in Eclipse.
If I tried to export the project as Runnable jar and tried to use the class with the service loader, it never worked.
When I checked the jar, unzipping it, I found the folder under src/META-INF/services though.
Only when I also added the META-INF folder directly in the root directory of the jar, it started to work.
I haven't found a fix though inside Eclipse, that makes sure it gets exported right...maybe an ANT script can solve this issue, but so far no attempts made...
This is an answer:
At some point, Android removed the AccessControlContext field in ServiceLoader and ServiceLoader now works. As my comments indicate, this was reproduceable using the "out-of-the-box" OREO (API 26) Intel Atom x86 emulator with Android Studio (also fresh download). 24 hours later, ServiceLoader no longer contained the acc field (as shown in the Android Studio debugger with the same emulator). The Android SDKs dating back to API 24 do not show the acc field.
Per the Android developer currently maintaining the ServiceLoader code:
He is not aware of ServiceLoader ever having the acc field in Android (it did as we were able to reproduce) and thought the debugger/emulator might have been using JDK code (but I showed the OpenJDK code works correctly). Somewhere along the way, the errant code was updated and I am no longer able to reproduce.
Be sure your OS is up-to-date and you should no longer see this phenomena.

Mocking Google Analytics v4

I'm trying to test my code that depends on Google Analytics SDK v4.
Specifically I'm trying to mock com.google.android.gms.analytics.Tracker with Mockito.
Tracker tracker = Mockito.mock(Tracker.class); yields this error.
Are there any approaches to take? The only thing I can think of is to create my own wrapper.
I believe the code snipped at fault is Tracker.class - as this will instantiate the class, which in turn throws the VerifyError. This is not an issue with your code, but is a limitation of Google Play Services. The issue has been reported in the Robolectric project and here.
I used the solution provided in the second link by SuperJugy, by inserting the following to the bottom of my Gradle build file:
tasks.withType(Test) {
test {
// set JVM arguments for the test JVM(s)
jvmArgs '-XX:-UseSplitVerifier'
}
}
To get it working in Android Studio I had to add the VM Option -noverify to my test build configuration.
I think you may be able to work-around the problem using a wrapper, so long as the wrapper code never instantiates the Tracker class. However this may not be easy (or possible?), please let me know if end up going down this path and succeed!

OSGi (Felix) on Android: NoClassDefFoundError: sun/reflect/ConstructorAccessorImpl

I try to run a bundle that obviously uses sun.reflect package.
First some more backgroundinformation: I'm using Apache Felix on Android.
And i also added sun.reflect as a system extra package.
String extrapackets = "sun.reflect; version=\"1.0.0\"";
config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, extrapackets);
config.put("felix.bootdelegation.implicit", "false");
"sun.reflect" is also shown in the exportinglist of the system bundle (using headers cmd).
And it is imported by my bundle. It also got correct versionnumbers at the export and importing stuff.
Errormsg:
java.lang.NoClassDefFoundError: sun/reflect/ConstructorAccessorImpl
at sun.misc.Unsafe.defineClass(Native Method)
at sun.reflect.ClassDefiner.defineClass(ClassDefiner.java:45)
at sun.reflect.MethodAccessorGenerator$1.run(MethodAccessorGenerator.java:381)
at java.security.AccessController.doPrivileged(Native Method)
at sun.reflect.MethodAccessorGenerator.generate(MethodAccessorGenerator.java:377)
at sun.reflect.MethodAccessorGenerator.generateConstructor(MethodAccessorGenerator.java:76)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:30)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
...
In case anyone else runs across this issue, there may be a quicker fix.
I hit this bug when using reflection in OSGi running on a Sun/Oracle JVM. It turns out this is a known issue. The suggested fix of setting -Dsun.reflect.noInflation=true at the JVM level will cause the class not to be referenced, at the cost of ignoring an optimization.
I tried the fix, and it worked for me.
As far as I know, the sun.* packages are internal packages used in the Oracle JVM you install on your computer. Android, on the other hand, does not use Sun's (now Oracle's) implementation of the JVM. Android offers an API similar to Java SE, but has its own implementation of the VM (called Dalvik VM).
So, you won't find the sun.* packages on Android. You need to get the source code of the bundle you using, and compile it against the Android libraries (using an Android project in eclipse, or using Ant). Then, repackage it into a jar, and then you can deploy it without it referencing the sun.* packages.
Kartik
Did you also import the package in your bundle? Just adding it to your systembundles.extra is not enough.

Categories

Resources