Android Unit test API calls with Retrofit2 - android

I haven't done any testing in Android, so please bear with me if this seems a stupid question.
I'm developing an app which makes a lot of network calls from a restful API service. To make the network calls, I'm using Retrofit2 and RxJava.
What would be the best practice/framework to just test if these calls are working? I've started to read the Google Codelab for testing which uses Junit4 and Mockito, but I don't want to do any clicking in the UI to start a test currently, just checking for different API versions which calls are supported or not.

Here some steps for you that I am using:
Use mockito & junit4, for sure :)
I avoid UI tests for these cases
Pass your retrofit Api as a parameter to a class that you want to test
In the test create a mock retrofit api, pass this one as a parameter so you can choose what you want your "Api" to return e.g. objects or errors (see Mockito.when())
Use RxJava's TestSubscriber to test a method e.g. Observable<Location> getLocationFromApi()
Avoid threading in your testing class (e.g. like .observeOn(mainThread())). If inevitable use awaitTerminalEvents in TestSubscriber. If there is no terminal even rethink your test
General tips:
Try to modularize your code so each class has few functionality -> easier to test.
Be patient and don't expect to write tests for e.g. 5% of your code in just one week :) It's a slow process regardless team size

Related

Android: How to do (automated) functional testing of Location Services

The app that I am working, is driven by Location Services. Driving with phone hooked to laptop is not always practical (and getting complex).
How would I write automated functional test for my app (googling shows UI related testing)? I understand that I need to inject values into following callback (since my app is driven by Location Manager)
#Override
public void onLocationChanged(Location location) {}
In a java application, I would create a unit that would have instance of my application and invoke the methods. But,
a) How would I do it for Android App (where there is no main method and everything is driven by onCreate())
b) How does it change, if above functionality is part of Android Service
Where we work, we use PowerMockito. I'm not a super fan of it, but I totally understand your frustration with unit tests. Here's a decent tutorial for the topic: http://www.johnmullins.co/blog/2015/02/15/beginners-guide-to-using-mockito-and-powermockito-to-unit-test-java/
Basically, what PowerMockito lets you do is write a bunch of Powermock unit tests. Those tests are going to let you mock the responses, and let you test your code. You can even manually call "onLocationChanged".
The reason I don't like PowerMockito is that that means you have to act like the Android OS. You can make dummy location responses and test to see if your code does the right thing, but it's much harder to test the code with field conditions. If a particular phone has an issue with the locations its returning, PowerMock is never going to help you catch it.
Use MockLocationProvider in android genymotion emulator.
See below link:-
https://mobiarch.wordpress.com/2012/07/17/testing-with-mock-location-data-in-android/

How to write an android test which will verify my app hits all required apis

I am heavily testing my application with unit tests and Espresso tests. My next step is to make sure my application hits all required apis. For that reason I am looking for a way to write a test, which will verify the api calls.
I would really appreciate any suggestions.
What you are describing is called a "unit test". Unit tests are meant to test as many lines of code as possible regardless of UI.
Espresso tests are "instrumentation tests" (or "UI tests") intended to check if the app is responding to UI events correctly. They're not meant to verify the correctness of code, but the correctness of the functionality of the app as used by the user.
You can read about both at the official documentation. You'll find that unit tests are very different than instrumentation tests, and often harder to write because they require more engineering of your code to do correctly. You will likely have to "mock" the various parts of your application to make sure their APIs were called exactly as you expected.
There are 2 main goal when I was writing api tests:
First is component based. The goal was to make sure each class / component makes an api call when certain criteria is met (for example calling an api A when onDestroy() is called)
Second, is to make sure the Apis are called is certain order for the analytics purposes.
The first step I achieved by using unit tests with injected mock objects via Mockito and PowerMockito. PowerMockito was used primarily to mock static methods and to make sure the methods were called at least n times.
For the second step, UI test could be used, since it runs the real application. I have implemented the helper class, which was recording the instances when api requests were made. The script in Espresso was validating the order of api calls by referring the helper class.

Mocking API responses for Android Test

I want to mock the API responses for my Android Async Network Tasks for Unit Testing. Anyone can tell me how to do this? I'm beginner to testing in Android.
Create your own class implementing the same API as your 'real' networking class. When one of its methods, say, method requestData() gets called, wait a bit inside the method then return some mock data in the same way as the real class is supposed to do.

Testing Strategy for Web Services based Android app

I have a web services based app i.e. each activity calls a particular web service and populate data into view. I haven't done any testing yet but I was in the process of switching to Volley and thought I might add some testing too.
I was wondering what testing strategies should be for such apps. I'm playing around with the gradle-android-test-plugin and although Robolectric doesn't play well with Volley yet, it should soon (I hope).
Anyways, so the structure I use for my app is this:
ObjectJacksonRequest class which get the data from the webservices and parses them into POJOs
RequestQueue standard volley request queue
BaseActivity the parent activity of all my app's activities which houses the RequestQueue object and other common stuff
Various Abstract activities which I use to separate the UI code from the network code. For example, the AbstractAppleActivity is extended by any activity that needs an apple. The abstract activity does things like check if the Apple is still in memory when returning, load new ones, set up adapters for list views, etc.
Here's the questions I currently have. I have some intuition on the answers should be but I'd like to hear from the community. I'm sure there are others questions too that people might have and we can add those to this list:
What's a good way to test the ObjectJacksonRequest object for the various web services ? Should I be writing a separate test for each web service that returns and object ?
Should I be testing network requests with the actual webservices or mock the responses ?
Should I be testing the BaseActivity and the abstract activities or the activities that extend it for it's functionality ?
What is the best way to test an activity that doesn't have any values inserted until the web request is completed ? For example, an activity that loads a list of apples.
What's a good way to test the ObjectJacksonRequest object for the various web services ? Should I be writing a separate test for each
web service that returns and object ?
If I am understanding it correctly you want to test the webservice backend. In that case I would write a test per call, or even multiple tests per call one testing each response senario.
Should I be testing network requests with the actual webservices or mock the responses ?
I would mock out the responses. It is my experience that if you test against a real webservice, you will get random fails because the connection was to slow or timed out or something and you end up running your test twice just to be sure it wasn't a random fail.
Should I be testing the BaseActivity and the abstract activities or the activities that extend it for it's functionality ?
I would not test the BaseActivities unless they have a lot of logic.
What is the best way to test an activity that doesn't have any values inserted until the web request is completed ? For example, an
activity that loads a list of apples.
Mock out the webservice response, so that it has values. You can do this with Robolectric. I believe the relevant method is Robolectric.addRequestInterceptor
I have more experience with testing now so here are my suggestions gathered for various sources:
- What's a good way to test the ObjectJacksonRequest object for the various web services ?
- Should I be writing a separate test for each web service that returns an object ?
- Should I be testing the BaseActivity and the abstract activities or the activities that extend it for it's functionality ?
All of these questions can be bundled into one. This is the question of testing inheritance hierarchies. The answer is, unfortunately, it depends. In most cases however, for abstract classes, testing the sub-classes will test the parent classes as well. Unless you have methods that have different use cases in different sub-classes, in that case you might want to test the abstract class so you can test all the cases as you code them (although if this is happening, you might be better off defining more objects).
- Should I be testing network requests with the actual webservices or mock the responses ?
This is an easy one. Always mock. As the other answer mentioned, you run the risk of writing flaky tests if you are using real webservices. Not to mention that testing the webservices is not your responsibility. You should only be testing code that you are responsible for.
- What is the best way to test an activity that doesn't have any values inserted until the web request is completed ? For example, an activity that loads a list of apples
This should follow from the mocked service. Once you have the mocked service. You can use it to send data to the activity in a reliable way and test the outcome.
Other Notes:
While the above are specific answers. In general I've found it useful to make some engineering decisions that facilitate not only testing but modularity of your apps. So these are some of my tips and to-donts from the engineering mistakes I had in my app design as per the question.
Use custom wrappers for services: In my question I am trying to move from Android's original HTTP to Volley. Part of the reason for these issues is that the app design couples the app with the networking library. One way to get around this is to use wrapper classes that are injected into your code. The underlying implementation of the wrapper class can then change without affecting the other source code. I have a blog post on this topic here.
Use Java modules: The Android gradle plugin in based on the gradle java plugin. You can therefore create Java library modules in your Android app project. I have found this is the best way to unit test your code that doesn't interact with Android API's. This is way easier and faster than both the Android test framework and Robolectric. I find that when I'm using a Java library module, my UI code becomes lighter because I'm trying to have as much work done in the Java module as possible.

Robolectric & testing startActivityForResult

I have troubles with my Robolectric unit test.
I can assert without problem that a click started a new activity when the listener uses the method startActivity( Intent )
But it seems Robolectric has trouble when a new activity is started with the method startActivityForResult(Intent, int) : putting some breaks in the code made me figure out that the activity wasn't started ( and just changing for method startActivity( Intent ) made the assert pass).
Is that normal ? It's a pity since the first activity of my app uses startActivityForResult(Intent, int).
Did someone manage to make tests with this way of launching activities ?
Thanks for your help ..
The short Answer to your question is that, due to the way Robolectric converts Android classes to code that executes in the JVM, a lot of their functionality doesn't behave as you'd expect. Many system callbacks won't execute, and you'll have to rely on what Robolectric provides in their Shadow implementation of classes. (See the link provided by #Steven_BDawg).
The long answer: It may be possible to implement this whole flow in one big test, but it's not what the framework is designed for.
Robolectric and Unit Testing in general aren't meant to be used in the way that you describe. The Unit Testing page on wikipedia states that one can view a unit as the smallest testable part of an application. A unit testing suite should contain many lightweight tests, where each test isolates a bit of functionality in your app and ensure it's working properly.
Consider a basic Application that contains two Activities, A and B. Activity A displays some information about a topic, and Activity B allows the user to select which topic to show in A. When the user moves from Activity A to Activity B, B gets called with startActivityForResult() and should return to A with the selected topic.
Now say we want to Unit Test this flow of A getting the result from B and displaying the data. We can break this up into two tests:
Activity Under Test - Activity A. In our test, we'll create a new instance of Activity A. In the Robolectric Test, we create the Intent that we expect B to return to A, and call the shadow method receiveResult() for A, filling out the arguments with a result code of OK and this Intent. After receiveResult(), run your assertions. You now know that Activity A handles the result properly!
Activity Under Test - Activity B. In our test, we'll create a new instance of Activity B, setting it up as if it were started for result from Activity A. In the Robolectric Test, we'll perform all actions needed to select the data, create the intent we'll send back, then run assertions on the intent to ensure it was created correctly.
This is a very simple example. These two steps could probably be broken out into many more tests, as, again, each unit test should only be testing the smallest unit of functionality that your app can be broken into. The example is mainly to help you start thinking in a unit testing kind of way. I've found that as my understand of unit testing deepens, the way I write code has changed. I try to avoid writing methods and classes in such a way that they do too much work and cannot be properly unit tested. As a rule of thumb, code that's easy to unit test performs very specific operations which are readily apparent when reading the code for the first time.
Finally, if you want to take this a step further, mocking frameworks can greatly aid your ability to Unit Test. Mockito is a mocking framework I've had success with in the past. The purpose of a mocking framework is to create stub Objects whose behavior you tightly control. Mockito (or any other Mocking Framework), will allow you to define an object that extends from any type you need and only implement the methods you need. You'll be able to directly control the response to any of these method invocations. This aids Unit Testing because the only real object that you'll need is the Object Under Test; by Mocking all other objects, you'll have a better sense of whether or not the Object Under Test is behaving properly because all other behavior is explicitly defined by you, the tester. (And yes, this does lead to lots of extra code, but such is the life of a good unit tester. However, as previously stated, as you get more comfortable with unit testing, you may find yourself writing methods that require less mocking and are more conducive to writing tests. Some coders will even write their unit tests BEFORE they code, in order to keep their code tight and focused on a single purpose)
Hope this helps!
I did some Google'ing for you. I don't know if this will definitely help you or not, but I think it is a good start!
Roboelectric: Testing startActivityForResult() and onActivityResult()

Categories

Resources