Chapter 11. Debugging and Testing on Android

In this chapter, you will learn how to debug in Android, an essential practice to save time in finding and fixing problems while developing our application.

We will learn how to create automated tests that can test the click of a button or the outcome of a single method. This is a set of tests that you can run in Android Studio to ensure that every time you develop a new feature, you don't break any of the existent ones.

You will also learn how to use Robolectric for unit tests and Espresso for integration tests.

At the end of the chapter, we will discuss how to test the UI with millions of random clicks using Monkey, how to record sequences of clicks through the app, and how to configure tests based on these recordings with MonkeyTalk.

  • Logs and the debug mode
  • Testing
    • Unit tests with Robolectric
    • Integration tests with Espresso
  • UI Testing
    • Random clicks with MonkeyRunner
    • Recording clicks with MonkeyTalk
  • Continuous Integration

Logs and the debug mode

We couldn't finish the book without mentioning logs and how to debug to solve problems while developing. Developing in Android can be more than just copying and pasting from Stack Overflow if you know how to solve your own problems.

The debug mode and logs are mechanisms used to help the developer identify where the problems are. With time, every developer improves and uses these techniques less frequently, but at the beginning, it's quite common to have an app full of logs. We don't want users to be able to see the log when the app is released, and we don't want to remove logs manually and then add them again when we release a new version. We will take a look at how to avoid this.

Working with logs

The log class is used to print out messages and errors, which we can read in real time using LogCat. This is an example of how to log a message:

Log.i("MyActivity", "Example of an info log");

The Log class has five methods, and they are used to have a different level of priority on the logs. This allows us to filter by priority in LogCat. There are situations when we display different logs, for instance, to see the number of job offers we download in every request. If we have a crash in our app, logs of the type error are our priority at this moment, and we want to hide other logs with less priority to find the error as soon as possible.

The five priorities are (from low to high) verbose, debug, information, warning, and error. (Log.v , Log.d, Log.i, Log.w, and Log.e)

We can filter by process with the bar at the top of the logging window. We can filter by priority and by keyword, and we can create custom filters by tag, process ID, and so on.

Working with logs

If the logs don't appear or they are old and not refreshing, try to open the dropdown to the right, select No filters, and then select Show only selected application again. This forces the console to refresh.

Working with logs

To finish with logs, we will create a wrapper and use a third-party library with the idea to be able to disable all the logs in the project by just changing the value of a Boolean. To do this, we simply create a class with the same methods of the Log class that depend on this Boolean value:

public class MyLogger {
  
  static final boolean LOG = false;
  
  public static void i(String tag, String string) {
    if (LOG) android.util.Log.i(tag, string);
  }
  
  public static void e(String tag, String string) {
    if (LOG) android.util.Log.e(tag, string);
  }
  …

We need to use this wrapper every time we want to write a log—MyLogger.d() instead of Log.d(). This way, if we change the value of the Boolean LOG in the MyLogger class, it will stop all the logs in our project at the same time.

It is recommended to use the value from the BuildConfing.DEBUG variable:

static final boolean LOG = BuildConfing.DEBUG; 

This will be true if our app is in the debug mode, and it will be false when we release the app. So, we don't need to remember to turn the logs off in release mode, and there is no risk of a log appearing to the final user.

Using Timber, the log wrapper

Timber is a log wrapper created by Jake Wharton that takes the log to an advanced level, allowing us to have different log behaviors using the log tree concept. Take a look at the following code:

compile 'com.jakewharton.timber:timber:3.1.0'

One of the advantages of using timber is that we don't need to write a tag in our logs more than once in the same activity:

Timber.tag("LifeCycles");
Timber.d("Activity Created");

Our trees can have different behaviors; for instance, I might want to disable logs in the release mode, but I still want to handle errors; so, I will plant an error tree that will report the error to Parse:

if (BuildConfig.DEBUG) {
  Timber.plant(new Timber.DebugTree());
} else {
  Timber.plant(new CrashReportingTree());
}

/** A tree which logs important information for crash reporting. */
private static class CrashReportingTree extends Timber.Tree {
  @Override protected void log(int priority, String tag, String message, Throwable t) {
    if (priority == Log.VERBOSE || priority == Log.DEBUG) {
      return;
    }
    //Track error to parse.com
  }
}

Debugging our app

Logs can be used to find problems while developing, but if we master the debug mode, we will find this practice much quicker.

While we are in the debug mode, we can set breakpoints in the code. With these breakpoints, we specify a line of code where we want the execution to stop to show us the values of the variables at that moment. To set a breakpoint, simply double-click on the left-hand side bar:

Debugging our app

We set a debug point in the response of the method that gets the job offer. We can launch the debug mode from the top bar:

Debugging our app

If we run the app in Debug mode, Android studio will pause the execution when it reaches this point:

Debugging our app

Android Studio will automatically prompt the Debugger window, where we will be able to see the variables at the point of execution. We can see in the preceding screenshot the job offer list and navigate to see what every offer has inside.

The important features here are the green Play button to the left, which continues the execution of our app until the next breakpoint, and the red square, which exits the debug mode and continues with the execution of the app.

We also have different controls available to move to the next line, into a method, or outside the method. For instance, consider that we have a breakpoint in the first line of the following command:

MasteringAndroidDAO.getInstance().clearDB(getActivity());
MasteringAndroidDAO.getInstance().storeOffers(getActivity(), jobOffersList);

In this case, Step Over, which is the blue arrow pointing downward, will move our execution to the next line. If we click on Step Into, the blue arrow pointing to the bottom-right corner, we will get into the getInstace() method. With a combination of these controls, we can control the flow in realtime.

With the debug mode explained, we can now move on to automated tests.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.214.155