Android unit testing with Junit: testing network/bluetooth resources - android

I am slowly becoming obsessed with unit testing. I am trying to develop as much software as I can using test-driven development. I am using JUnit to unit test my android applications.
I have been working on an app that uses bluetooth and am having a hard time unit testing it. I have an Activity that uses BluetoothAdapter to obtain a list of paired and discovered devices. Although it works, I would like to know how to unit test it.
To get the list of paired devices, I call getBondedDevices() on the instance of BluetoothAdapter. The problem is I don't know how to stub or mock this method (or any other bluetoothAdapter method that my Activity calls) so I can't test my Activity against different lists of paired devices.
I thought about using Mockito or trying to subclass BluetoothAdapter to somehow stub out the methods I'm interested in, but it's a final class so I can't do either.
Any ideas on how I could test programs that use BluetoothAdapter or other resources that are (as far as I know) difficult or impossible to stub or mock? As another example, how would you test a program that uses sockets?
thanks in advance for any help
aleph_null

To test your activity, you may refactor your code. Introduce a BluetoothDeviceProvider with a default implementation.
public interface BluetoothDeviceProvider {
Set<BluetoothDevice> getBluetoothDevices();
}
public class DefaultBluetoothDeviceProvider implements BluetoothDeviceProvider {
public Set<BluetoothDevice> getBluetoothDevices() {
return new BluetoothAdapter.getDefaultAdapter().getBondedDevices();
}
}
then inject this new interface, in the activity :
public class MyActivity extends Activity {
private BluetoothDeviceProvider bluetoothDeviceProvider;
public MyActivity(BluetoothDeviceProvider bluetoothDeviceProvider) {
this.bluetoothDeviceProvider = bluetoothDeviceProvider;
}
protected void onStart() {
Set<BluetoothDevice> devices = bluetoothDeviceProvider.getBluetoothDevices();
...
}
...
}
Now the activity seems unit-testable. But the BluetoothDevice is still final and you can't inject a mock in your activity. So you have to refactor this new code and introduce a new interface that wraps the BluetoothDevice... -> an abstraction layer upon the core android classes.
At the end the activity behavior can be checked via various unit tests... So the implementations of the newly introduced interfaces remains to test. To do this, you can :
keep them not (unit) tested, not a big problem for me since they just do delegation
look at PowerMock.
Also check this wiki page about mocking final classes using PowerMock.

Related

Using different classes for testing and normal runs in android

I'm trying to build an library that helps my application communicate with other devices using bluetooth
In order to achieve this I created an interface like so:
interface IBluetoothAdapter {
....
}
On normal runs I use it like so:
internal class BTAdapter : IBluetoothAdapter {
private val mAdapter = BluetoothAdapter.getDefaultAdapter()
}
and I use it to send messages to the actual bluetooth adapter
when I run my tests I mock IBluetoothAdapter and this works decently
in order to facilitate the above my actual class has this implementation:
class Communicator(val address:String) {
private var mAdapter: IBluetoothAdapter = BTAdapter()
#VisibleForTesting
#TestOnly
fun setAdapter(adapter: IBluetoothAdapter) {
mAdapter = adapter
}
.....
}
Is there a better way to do this?
Basically what I want to do is when the application runs in tests to always use one specific class (the mock) and when it runs normally to always use another (the one I created)
What your code is doing is called Dependency_injection
Is there a better way to do this?
There is not one "better" way to do it but there are different ways to do it that depend on the tools you are using (see why-do-i-need-an-ioc-container-as-opposed-to-straightforward-di-code for details.)
I would have implemented it the same way you have done it because your code can be used without any di containers so it is has less external dependencies.

AWS Device Farm Espresso tests order

I'm starting using Device Farm. My problem is that it is ignoring the order that I used to write tests. In local all works fine because Espresso executes tests in a certain order. To declare that order I used alphabetical order. All my classes starts with a letter (E.g. "A_EspressoTest") so I can choose which class has to be ran first.
Into my classes I use
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
to declare in which order my tests have to be ran.
It seems also like Device Farm ignores all my annotations (E.g. "#Test" ) because it is running also methods that doesn't have that annotation.
Lorenzo,
As of today, there is no way to specify the order of test execution with Espresso. Additionally, your observation about the #Test annotation is correct, we don't currently use that when discovering what test classes/test methods are selected.
AWS Device Farm currently discovers tests based on JUnit 3 style naming conventions (classes starting/ending with the word Test and methods within those classes starting with the word test.
For example:
// This class would be automatically discovered (by name).
public class LoginTests extends InstrumentationTestCase {
// This method would be automatically discovered (by name).
public void testLoginFormUsingInvalidCredentials() {
// ...
}
// This method would **not** be automatically discovered.
#Test
public void loginWithValidCredentials() {
// ...
}
}
// This class would **not** be automatically discovered.
public class Login extends InstrumentationTestCase {
// This method would **not** be automatically discovered since the class was not discovered.
public void testLoginFormWithValidCredentials() {
// ...
}
}
With all that said, we've heard plenty of feedback and requests for supporting test discovery using all JUnit 4 annotations and it's an area of improvement we're definitely taking a look at.
Hope that helps!
Best,
Andrew # AWS Device Farm

How do you cast RuntimeEnvironment.application?

When running Robolectric tests, RuntimeEnvironment.application's type is determined by your configuration. Say I configured RoboApplication.class as my test application, I can cast RuntimeEnvironment.application to my type without fail.
RoboApplication app = (RoboApplication) RuntimeEnvironment.application;
app.doSomething();
However, once I integrate PowerMock, the cast line fails with
java.lang.ClassCastException: RoboApplication cannot be cast to RoboApplication
How can I workaround this issue?
This is a problem because PowerMock and Robolectric are mutually incompatible due to the use of their own classloaders.
Even though the names are the same, the Class objects aren't actually the same: Robolectric and PowerMock both work by loading the test through custom classloaders. These classloaders change the classes in question, allowing you to replace static/final Android system classes and methods [Robolectric] or all static/final classes [PowerMock]. This is part of the reason that PowerMock and Robolectric both rely on having their own JUnit4 Runner: That way they can load the appropriate classes from their own modifying classloaders.
Because of this, the instances can't be cast to one anothers' classes, even though they have the same name and originate from the same source file: Each framework can change the class implementation, so they aren't necessarily compatible with one another.
You'll need to choose one framework or the other: Use Robolectric shadows, possibly with EasyMock or Mockito directly, or use PowerMock to stub the Android infrastructure calls yourself manually.
See also:
ClassCastException when casting to the same class
cast across classloader?
I needed also an app reference in order to start a Dagger2 module. After several attempts and getting the same cast exception error you are getting I made my app as follows
public class App extends Application {
private static AppComponent appComponent;
#Override
public void onCreate() {
super.onCreate();
if( appComponent==null ){
appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
}
public static AppComponent getAppComponent() {
return appComponent;
}
public static void setAppComponent(AppComponent component){
appComponent = component;
}
}
And within my Robolectric/PowerMock tester:
#Before
public void before() throws Exception {
App appMocked = PowerMockito.mock(App.class);
App.setAppComponent(DaggerAppComponent.builder().appModule(new AppModule(appMocked)).build());
....
}
Then my activity simply called up for App.getAppComponent().inject(this);
FYI, I tried not to mocked the app class and used ((App)RuntimeEnvironment.application), but that didn't work. I also tried to subclass it, and use it in Robolectric's application configuration, but ended up with the casting issue. So I hope this can be of any help.
Of course, that setter shouldn't go in production. But this is the only way I could figure this to work.

Inheritance of TestCases on Android

I was wondering if it was good practice to subclass the test cases on Android. I mean, I need to test a lot of Parcelable objects and I could create a class like GenerericParcelableAndroidTestCase to test all these objects.
I also have a problem implementing it, I have something like this:
public class GenericParcelableTest extends AndroidTestCase {
private Parcelable p = null;
GenericParcelableTest(Parcelable p) {
this.p = p;
}
public void testDescribeContents() throws Exception {
assertEquals(0, p.describeContents());
}
}
And that:
public class AttachmentTest extends GenericParcelableTest {
public AttachmentTest() {
super(new Attachment());
}
}
Attachment implements Parcelable of course.
It returns me this error:
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
I mean, I know that I created no empty constructor but why would I need one?
And generally, is there some known issues with this approach? If not why is there very few article on this topic on the internet (and actually some say even that it's not a good idea).
I have this conversation quite often when introducing new team members to unit testing. The way I explain it is by stating that your tests are first class citizens of your code base (no pun intended), they are susceptible to the same technical debt as any other part of your code base and have equivalent (maybe more?!) importance as that of the runtime code.
With this mindset, the questions begins to answer itself; if it makes sense from an OO perspective to use inheritance (i.e. your subclass is a insert name of test superclass) then subclass away. However, like any abuse of inheritance ever, be careful...the minute you add a test case that doesn't rely upon that superclass behaviour you may have a code smell.
In this scenario, it's likely (perhaps 90% of the time?) it is a separation of concern issue within the code being placed under test, i.e. the "unit" under test isn't actually (one) unit but has combinatorial behaviour. Refactoring that code to do one thing would be a good way of allowing your super-class test case to live on. However, watch this super class test case like a hawk...the minute you see booleans being added to signatures to "allow that similar but not the same" test case to run under your once unpolluted super class then you have a problem, a tech debt problem that is no different to your runtime code.
At last check AndroidTestCase depends on an Activity context so it's likely best described as an integration test which tend to regularly have boilerplate super-class test behaviour. In this case, try to narrow the focus of your superclass to the use case under test...i.e. extends LoginUseCase or extends LoginScenario to better "bucket" those subclasses in the first instance. This will help guide would be extenders as to whether they should be using it for their non-login scenario. Hopefully, conversation will ensue and tech debt accumulation be avoided!
Regarding your error, in JUnit3 do what #Allen recommends, if moving to JUnit4 with something like Robolectric then explore using Rules as well as #BeforeClass.
Personal note
I have only felt the need to write test super classes for pseudo-unit tests that mock an API end point (akin to MockWebServer if you are familiar with that product) and DAO integration tests whereby an in-memory db is started and torn down over the lifecycle of each test (warning - slow (but useful) tests!)
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
You get this error because JUnit needs to be able to construct an instance of your test class. It only knows how to do this using no-arg, or single string constructors.
Instead of performing initialization in your constructor, you should put it in the setUp() method. This will let you use the default constructor while still initializing the object before the test method is called.

Roboguice and mocks: How to have roboguice inject a mock service when testing but use the REAL otherwise?

Just got my feet wet with roboguice, i like it!
I have quite a lot of methods that depend on a DB and LocationManger etc hence when i am testing these it uses the real objects, i would like to mock these objects so that when i am testing i don't have to depend on anything.
I also have been using mockito but i am unsure how i could go about this?
I know the android system comes with various mocks but i think it would be better to roll my own with mockito?
In either case i need to inject them when testing.
Anyone have any ideas on this?
Thanks in advance
Take a look at https://github.com/roboguice/roboguice/blob/master/astroboy/src/test/java/org/roboguice/astroboy/controller/Astroboy2Test.java which uses Modules.override() to override the default module with some test-specific configurations.
#Before
public void setup() {
// Override the default RoboGuice module
RoboGuice.setBaseApplicationInjector(Robolectric.application, RoboGuice.DEFAULT_STAGE, Modules.override(RoboGuice.newDefaultRoboModule(Robolectric.application)).with(new MyTestModule()));
// For roboguice 4.0 and robolectric 3.1.2
RoboGuice.getOrCreateBaseApplicationInjector(RuntimeEnvironment.application, RoboGuice.DEFAULT_STAGE, Modules.override(RoboGuice.newDefaultRoboModule(RuntimeEnvironment.application)).with(new MyTestModule()));
}
Just to expand on this as it was the top hit while I was looking for it...
Once you've set your test class (or test runner) to override the default RoboGuice module then set your overriden RoboGuice Module as (in this instance)
public class TestModule extends AbstractModule {
#Override
protected void configure() {
bind(LocationManager.class).toInstance((LocationManager) Robolectric.application.getSystemService(Context.LOCATION_SERVICE));
}
}
Then RoboGuice will inject the same location manager in your tests as in your application. And you can instantiate a shadow of it and set the expected location, provider state, etc.
#Test
public void mapLoadsCenteredOnPhoneLocationWhenNoTargetIntent() {
Location l = new Location("test");
l.setLatitude(Double.parseDouble("52.222"));
l.setLongitude(Double.parseDouble("-2.222"));
shadowLocationManager.setLastKnownLocation(GPS_PROVIDER, l);
shadowLocationManager.setProviderEnabled(GPS_PROVIDER, true);
shadowLocationManager.setProviderEnabled(NETWORK_PROVIDER, false);
//snip
}
#Martin: As Paul says you can inject your test location manager with Robolectric and Roboguice. However I think it's better if mocking with Mockito, this post is good for starting. You create a Mocked object and bind it to your interface. You can find also example with mocking and injecting.

Categories

Resources