- Testing your Api calls on Android
- What exactly do we test?
- Getting ready to test
- Mocking the Http call using MockWebServer
- square/okhttp
- An HTTP+HTTP/2 client for Android and Java applications. — square/okhttp
- Reading Test Resources
- Using a dependency injection framework
- InsertKoinIO/koin
- KOIN — a pragmatic lightweight dependency injection framework for Kotlin — InsertKoinIO/koin
- Testing Api calls
- Going further
- REST API on Android Made Simple or: How I Learned to Stop Worrying and Love the RxJava
- Part I: Making API calls with Retrofit
- Part II: Handling asynchronicity and more with RxJava
Testing your Api calls on Android
Jan 2, 2019 · 7 min read
Let’s start this article with a confession. I don’t test my code as much as I should. I test all my algorithms but that is pretty much all I test. I test those because it enables me to work a lot faster than if I had to build the app, install it and then visually verify (try to) that the algorithm had performed as expected. And with that approach I can quickly validate that the edge cases are properly handled without having to painfully try to replicate them on the app.
Now let’s be honest. My apps don’t have that many algorithms. What they have a lot of is Api calls. So I always thought how great it would be if I had the same level of assurance on my Api calls that I have on my algorithms. So on a new project I am working on I decided to change that and this post is what I got in result.
There are some big questions to answer when it comes to testing Api calls:
— What exactly do we test ? Which is the same as wondering what we are trying to validate.
— What tools do we need?
— How do we implement the tests?
What exactly do we test?
Let’s make something very clear: We do not test the Api. We do not send test requests to the backend to make sure it returns a response containing the expected json data. This is the responsibility of the backend developer.
We test the Api calls and the app behavior when a response is returned, no matter what the response. In other words, we test that the app sends out the proper request and properly handles the response or error. And that is already a lot.
We will not test Activities here because in my experience, instrumented testing is still too slow and we are trying to keep our testing iteration cycle as fast as possible. Therefore all our tests will be local tests. This serves a second purpose, which is to force us to separate everything that is not purely UI or Android related (fetching location, notification api…). This means no Api calls inside our Activities or Fragments which is a lot better.
Getting ready to test
This is the part where we get our hands dirty. I assume here that you are comfortable with writing basic unit test methods. Your test methods should be kept simple anyway so don’t worry they will be very easy to understand.
For this we will create an api call that performs a login operation. Here I use Retrofit. Again, if you are reading this post, I assume that performing api calls is not new to you.
This AuthenticationManager will be called by an AuthenticationInterceptor to retrieve the token or perform authentication with the hardcoded credentials.
As you can see, the Endpoint is harcoded as a constant but is also required in the constructor. This will become usefull in a second.
Mocking the Http call using MockWebServer
square/okhttp
An HTTP+HTTP/2 client for Android and Java applications. — square/okhttp
MockWebServer is a library provided by Square that lets you Mock a WebServer. The idea is that you create a mock server and you tell it what it should return when called. So you just pass it a String body and it will return it. This library also lets you inspect the calls it has received (path, params, body…). So we should have all our needs covered.
Before we can use it though, we need an efficient way to store and retrieve body responses. We don’t want to clutter our test classes with enormous json like strings. We want to save those potential responses as json files and read them for our tests.
Reading Test Resources
You start by creating a json file.
And we save it in your test resources directory exactly like this. This is important so that our test resources get picked up by the Android Gradle plugin.
Then we need a way to easy access those files.
In this class, we can access the classLoader and read the resources because we placed the resources in the “resources” directory. Otherwise we would have gotten a FileNotFoundException.
As a little bonus, and because we are into testing now ; we are going to test this MockResponseFileReader. This is really easy and will give us confidence that we are on the right track.
We create a quick test.json file that simply contains the word “success” and we test that we can successfully read it.
Using a dependency injection framework
This is clearly an optional step, but you will see that we use dependency injection by constructor in our tests so a framework can make our life easier when things become more complex or we need to refactor.
For this project, I have decided to use Koin which is very simple to use. So although it may not have all the cool features that Dagger has, it leaves you with enough brain power to understand the advantages of dependency injection while learning to use it.
InsertKoinIO/koin
KOIN — a pragmatic lightweight dependency injection framework for Kotlin — InsertKoinIO/koin
Testing Api calls
Now that we have a way to mock a server and a way to store and read mock response bodies, we are ready to write our Api tests.
Testing the request sent
I said that we should test the request sent to the server and the app behavior when handling the response, but in the case of our AuthenticationManager, testing the request sent out is basically testing that Retrofit is doing its job (which is not our responsibility). Since the AuthenticationManager is to be used with an OkHttp Interceptor, later on we will make sure that we are sending out a token in the header.
But for the sake of learning how to use the MockWebServer, here is a way to test that the body send is as expected.
So this is how you use the MockWebServer:
1/ Create an empty MockWebServer()
2/ Enqueue responses using the method enqueue() with a MockResponse. I don’t know if there is any limit to the number of mock responses you can enqueue.
3/ Get the base url of the server and pass it to your retrofit instance to perform the calls in your app. This is where Dependency injection is useful. Here the base Url is a dependance of the AuthenticationManager and we pass it through the constructor. Note that we could have done just the same without Koin (in fact Koin clearly gets in the way in simple cases like this one).
4/ Perform your Api call normally
5/ Check the request received by the server using server.takeRequest()
Testing the behavior on result
A very simple use case is to test that the call result correctly populates our model, whether on success or failure. It still feels a little like we are testing Retrofit, but a least we get the assurance that our model is correct.
Upon success, our AuthenticationManager should hold a Login object which contains a token or an error description.
To do those tests, we need a login_success.json and a login_failure.json files that our MockResponseFileReader can parse for us. Those mock responses are copy pasted response I obtained using Postman.
And here are the tests that use them.
If you are familiar with the junit syntax, then the only new things in this test class are the Koin related operations.
Basically, before each test we create a new instance of the AuthenticationManager using the Koin function factory. This means that a new instance of the AuthenticationManager will be created each time we call get().
A new instance of MockWebServer is also created for each test. But since we need to access the same instance multiple times during the test and at the end; we could not simply use factory. So we use the koin feature called Scope which lets us define when a particular instance should be created or dropped.
Going further
I will stop here for the code part of this tutorial because although I have written many more tests that I could share here in detail, I think there is already enough to take in for one day.
But I will still describe some use cases that I have covered in my project that I think are useful and go further than validating the model:
— Making sure the interceptor passes the token whether it is already stored or it has to perform a login first (BTW my project is an IOT device so I can’t decently ask the user to re-enter credentials if the token expires which it seems the Api is doing very often)
— In case of error on any call, make sure the api call is retried.
— In case of multiple errors, make sure the Activity gets notified (I am not testing the Activities yet).
NB: As you can see, we will test our api calls in conjunction with our Interceptors, RxJava functions and LiveDatas inside our ViewModels to make sure that they broadcast the proper information. So in an Android app, our Api call tests are more integration tests than unit tests. And that is perfectly fine =)
In conclusion, with the proper tools and goal in mind, testing the api calls in an Android app is a fairly easy task that will let us gain a lot of assurance and may very well speed up our development by allowing us to test the calls without building, installing and navigating the app.
Источник
REST API on Android Made Simple or: How I Learned to Stop Worrying and Love the RxJava
May 29, 2018 · 10 min read
If you’re an Android developer you probably remember, with no small amount of nostalgia, the first time you encountered the NetworkingOnMainThread exception. Asynchronicity is one of the main driving forces behind the Android platform, and yet, the libraries provided by the SDK always were somewhat lacking when it comes to dealing with it. Combine that with tedious work of manually dealing with any kind of RESTful API, and you soon find yourself in a hell made of disjointed code fragments, repeated loops and confusing callbacks.
This article wi l l show you, step-by-step, how to make a simple API call on Android the easy way — using 3rd party libraries. In the first part we’ll deal with the API call itself using Retrofit and Gson. In the second part we’ll see how we can make our lives even simpler and deal with asynchronicity in a concise and elegant way using RxJava.
Disclaimer — This article does not cover the subject of RESTful APIs in great detail. It is not an in-depth Retrofit or RxJava tutorial. Rather, it’s a short and simple guide on dealing with an API request using said two libraries in conjunction.
Part I: Making API calls with Retrofit
For this part we will be using Retrofit2 and it’s Gson adapter so let’s start by adding their respective dependencies to our build.gradle file (also add the RxJava dependencies, as we’ll need them later):
By the way, let’s not forget to add the Internet permission to our manifest file:
Retrofit makes it really easy to consume APIs by doing a lot of heavy lifting for us. It won’t do all the work by itself though, so let’s see what we have to do to get things rolling:
We use builder pattern to configure and build a Retrofit instance. For more details about Retrofit configuration see the documentation, for now just make sure to include API’s base URL, GsonConverterFactory and RxJava2CallAdapterFactory. These are necessary if we want to use Retrofit with Gson and RxJava, as we will be doing in this example.
If you’re more familiar with some other JSON parsing library or just don’t feel like using Gson you can check out the list of supported converters here.
2. Create a model class
Gson uses model (or POJO) classes to convert JSON data to objects. One thing to remember when it comes to model classes is this: they have to reflect the structure of JSON response we want them to represent. So let’s say we want to get some data about a famous movie director from the popular and free-to-use The Movie Database API — if we consult the API documentation, we can see that our JSON response would look something like this (shortened for the sake of simplicity):
We could write the model class by hand but there are many convenient converter tools to save us some precious time, for example this one. Copy the response code, paste it into the converter, select Gson, click on preview and, if all went well, what comes out should very much resemble a simple POJO class:
As we can see, the fields represent our JSON response. Annotations are not necessary for Gson to work but they can come in handy if we want to be specific or keep the camel case naming. Just keep in mind that if a variable name doesn’t match the JSON key of the corresponding key/value pair Gson won’t recognize it and it will be ignored. That’s where the @SerializedName comes in — as long as we specify it correctly we can name the variable however we want. And that’s that for the model class. Let’s see how to actually make use of Retrofit to specify our requests.
3. Create an ApiService interface
To use Retrofit we simply make an interface to house our API requests (name of the interface is arbitrary, but it’s a good practice to end it with service). Then we use annotations from the Retrofit library to annotate interface methods with specifics of the each request. Going back to the API documentation, we find that our request should look something like this:
Let’s take a closer look at this piece of code and see what it means:
- @GET tells Retrofit what kind of request this is, followed by the coresponding API endpoint. It should be obtained from the API documentation
- Single
is an RxJava constuct and we’ll deal with it later, for now just keep in mind that it requires a type, which must be of the model class we are fetching with the request (in this case, Person)
Put simply, when this method is called, Retrofit will generate a request and forward it to the API. If successful, it will use the Gson converter to convert the response to the object of our model class. Under the hood, the generated request looks roughly like this:
For those still uncertain about what goes where, here is a quick recap:
- https://api.themoviedb.org/3/ — this is the base URL
- person/ — this is the endpoint followed by a path
- ? — a question mark indicates the beginning of queries
- api_key= — this is a query, in this case, for an api key
- & — more queries incoming
- language=en-US — this too is a query, but we left it at default
And since we annotated the parameters with @Path(“person_id”) and @Query(“api_key”), the arguments we pass when calling the method will replace and .
4. Hook up the service with Retrofit and make the request
All that’s left to do is to actually make the request. To do that we must first create an instance of the ApiService using the Retrofit object we created at the beginning.
That last line of code is where we actually make a request to get the data we need, wrapped in a Single object. But what is a Single? And how does it help us deal with the asynchronous nature of network calls? We answer these questions, and more, in the second part.
Part II: Handling asynchronicity and more with RxJava
We’ve all heard of AsyncTask. Most of us have used it at some point in our developer careers. The unfortunate few still use it today. But almost everybody hates it. It’s overly verbose, clumsy, prone to memory leaks and bugs, and yet, in lack of a better tool, many online tutorials still use it in dealing with all that background work Android platform is ripe with.
In this part we’ll see how to ditch the AsyncTask altogether in favor of RxJava, and in the process, save yourself a lot of time and avoid headache. Keep in mind that this is not an RxJava tutorial and we will only scratch the surface of what RxJava is capable of — working with Retrofit to ensure a simple and elegant solution to asynchronous network calls.
In the last part we left off at this line of code:
So what is a Single? Without making this an RxJava tutorial, let’s say it allows us to recieve a single set of data from the API, do some stuff with it in the background, and, when done, present it to the user — all that in a few lines of code. Internally, it is based on the observer pattern and some functional programming goodness, with data being pushed to interested observers at the moment of subscription.
To receive the data we only have to subscribe to a Single, calling the subscribe() method on it and passing a SingleObserver as an argument. SingleObserver is an interface containing 3 methods. By subscribing we ensure that the data is pushed when ready, and is passed to us in onSuccess() method. That is, if request was successfully completed — if not, onError() is invoked, enabling us to deal with the exception as we see fit.
But what about the onSubscribe() method? It is called in the moment of subscription and it can serve us to prevent potential memory leaks. It gives us access to a Disposable object, which is just a fancy name for the reference to the connection we established between our Single and a SingleObserver — the subscription. That subscription can be disposed with a simple method call, thus preventing those nasty situations when, for example, rotating the device in the middle of a running background task causes a memory leak. What we want to do is the following:
- first we create a CompositeDisposable object which acts as a container for disposables (think Recycle Bin) and add our Disposable to it in the onSubscribe() method:
- then we simply call the dispose() method on the CompositeDisposable in the appropriate lifecycle method:
We’re almost there — we got the data we wanted and we did our chores of cleaning up any potential mess. But we still haven’t dealt with asynchronicity. NetworkingOnMainThread exception will still be thrown. Now comes the hard part, you must be thinking? Not really. Thanks to the RxAndroid library, RxJava can be made aware of Android threads, so all we have to do is add two more lines of code:
As easy as that, with subscribeOn() we told RxJava to do all the work on the background(io) thread. When the work is done and our data is ready, observeOn() ensures that onSuccess() or onError() are called on the main thread.
While we’re at it, let’s explore RxJava some more. Let’s say, for the sake of example, that we want to make some more use of the API and fetch a list of movies related to our Person. We would have to make another API request following the same steps — create a model class and add another method to the ApiService, with the corresponding API endpoint. You’re probably thinking we have to repeat the steps with RxJava too, then think of a complex logic to deal with acychronicity and time the callbacks so we can update the UI at just the right time? Not necessarily.
Enter RxJava Operators. Operators are methods that allow us to, well, operate on our data and manipulate the way it’s being pushed to us. They can be called on Single objects and chained just like we did before. The list of available operators and their uses is huge and you can find it here.
In our case, wouldn’t it be nice if we could simply join the Person with the list of movies in the background and get them at the same time, as a single set of data? Luckily, there is just the Operator to meet our needs — Zip. Presuming the request for movies returns a list of Movie objects, we can add a List field to the Person class and do the following:
If it seems confusing at first glance, don’t worry, it usually is. It takes some time and practice to get acquainted with the syntax, and knowing your lambda expressions helps a lot. What we did is call the zipWith() method on the Single wrapping a Person request — zipWith() accepts 2 arguments — another Single (the one wrapping a list of movies) and a lambda expression which gives us access to both person and movie list data, since the Zip operator joins two separate data sources. In the lambda we assigned the movie list to the person. Now, when all the background work is done, onSuccess() will give us a Person object together with the list of movies we assigned to it.
Источник