Mobilization inaki villar header

Espresso: Beyond the Basics

In this Mobilization 2016 talk, Iñaki Villar discusses the more advanced workings of Espresso by showing how it integrates into different frameworks, and how it works over the Android system. Iñaki also talks about the methods of testing and instrumentation, and explains the tools he uses for them. He goes on to explain the inner workings of the libraries he discusses, and shows how they work together in an application.


Introduction (0:00)

My name is Iñaki Villar and today, I’m going to talk about Espresso. How many Android developers do we have in the room? And how many of you are working with Espresso? Whoa, perfect, excellent.

This talk is about Espresso. Normally, we have the basic view matchers, but today, we are not going to talk about the basics of Espresso. This talk is going to be more advanced.

Structure of Espresso (0:33)

Let’s take the basic structure of Espresso and some tips to work with. In my current company, Agoda, Espresso has replaced the previous framework, Appium, and now we have covered 60% of the manual testing. That means when we are going to release a new version, we are not doing manual testing with the QA as we don’t have resources to test like that.

Initially, working with Espresso is like a love story, right? Because when you start working with it, everything is nice. But after two tests or two days, you just hate some stuff, right?

Well, that’s life, so let’s start with the structure and what’s inside of Espresso. The first concept is instrumentation.

The Android API for testing provides hooks in the application component and the life cycle of the application. To understand what it is, for example, if you have your device and the user goes back in their activity, what happens? The activity finished, right?

Internally, you know that the method onDestroyed is called, but who is calling this method, is it you? No, it’s the system. You don’t have access to these methods. Obviously, you can type onDestroyed in your code, but it’s not the case. That’s the instrumentation bit. You have access to these internal system calls, to start or destroy an activity, as if you are in a real application.

Since the last version of Android, we have been using the Android Testing Support Library (ATSL), and we have a new improved instrumentation runner. The base is the monitoring instrumentation. This monitoring has advanced features for Android instrumentation. For example, you don’t have to wait until the view is rendered on the screen. It’s nothing to do with the monitor instrumentation.

At the end, we are adding in our code in the build gradle of AndroidJUnitRunner. But before that, we have monitoring. One of the coolest things is that you can do it before the application is started, before the activity itself is. You have the application onCreate and after that, you are starting the instrumentation. This means that you always know where you are. You have an application at the same state. Maybe it’s not very clear for developers who are working with Espresso, but it’s cool to have something that is behind you controlling everything for you.

AndroidJUnitRunner’s custom arguments (3:55)

The instrumentation of AndroidJUnitRunner comes with lots of custom arguments. That means we can start testing outside of the Gradle plugin.

Remember that when you want to start your Espresso test, you have to click, and you’re executing two tasks: assemble, plus the connected automation test. But it might end up starting testing, and I don’t want to repeat the compilation time because right now, we don’t have instant run in Espresso. It is very sad, but I hope that in the future we can have the same features as we do in a normal application, because you are spending a lot of time.

There are a lot of arguments for the instrumentation, but I’ll start with the important ones.

We have the package and class ones. That mean that we can run only one test included in the class and the package. The way that we want to do is maybe with adb am, and then we can run the test. That means that if I want to repeat my test, I don’t have to compile.

Another argument is debug. It’s cool, because we can attach the debugger into the test, and it’s fast.

For annotation and coverage, remember that we have some custom annotations provided by the ATSL, like a small/large test. If we want, we can run only this test with the annotation and the coverage.

One feature which is not very well known is sharding. Sharding is the ability to split the test. It means that, when you have 50 tests and 2 devices, you can split the number of tests per each device and it’s faster. Actually, we’re working with agents in TeamCity for 4 devices: 400 tests, and we’re splitting it by sharding them. It’s very easy; you have to specify the number of shards that you want - maybe I want numShards to be seven because I have seven devices - and then the shardIndex that you want to execute. For example, if you’re executing only the shard with number two into the device that you have selected, that’s shard number seven, shard index two.

Okay, now we know a little bit what’s the instrumentation. The next step is to know what’s happened in the project, because Espresso is a dependency in our project. Let’s see the what the internals look like.

Structure of Views (6:38)

If we look at the basic method, the ViewInteraction in View, we can see from the code that we are calling BASE.plus. This belongs to the dependency injection framework that is using Espresso to provide all the dependencies.

Get more development news like this

When I start a new project from scratch, as in I join a new company, I think it is a good practice to analyze the graph of the dependency injection framework because you can have a good picture of what the internals are.

BaseLayerComponent (7:32)

Internally, the first component - the base component - is the BaseLayerComponent, and we have two main modules: BaseLayerModule and UiControllerModule.

The dependencies provided in the BaseLayerModule are these, some of them are very common in our case. For example, the Context, the Looper, the Recycler. Not the RecyclerView, the Recycler of the access queue, and also the ActivityLifeCycleMonitor.

BaseLayerModule and its interfaces (7:59)

Let’s talk about ActivityLifeCycleMonitor. It’s from the instrumentation runner, it’s not from Espresso though, and it allows you to query the state of the activity under test. Now, I trigger my activity and maybe it’s resumed, or maybe it’s stopped, and I can just check the state of the activity.

AsyncTaskPoolMonitor is an interface that allows you to know the state of the queue of the AsyncTask in the application, and this AsyncTaskPoolMonitor tells you if the queue is idle or there are some executing or pending-execution tasks.

ActivityRootLister is an interface that provides access to all the views in the user interface. When we are doing this on view matcher, this is the dependency that is allowing to us to do that.

FailureHandler is an interface to list the errors that are happening in the runtime.

And finally, EventInjector is one interface with that allows us to select the proper strategy to inject the events in the code.

UiControllerModule (9:26)

The other module is UiControllerModule, and it’s providing the UiController dependency that is a base-level user interface component that allows you to build actions like click, scroll, etc. It also handles all the synchronization problems that we can have.

I think it’s very important to know that including all the dependencies in the base application object is not a good practice. But in this case, it’s okay, and you can learn how to structure the code of Espresso.

After seeing the dependencies, the next step is to know where we’re injecting these dependencies.

Injecting dependencies into BaseLayerComponent (10:19)

Obviously, we have the FailureHandlerHolder, and we are repeating the ActivityRootLister and the IdlingResourceRegistry. “Idling resource” is a very popular term in Espresso. They work with background tasks, and later we are going to see more about this class.

Finally, we have the ViewInteractionComponent which also provides a module, the ViewInteractionModule.

Using Matchers for Views (10:47)

Remember that when you are trying to find a view in the user interface, you are creating a view interaction. That’s where all the magic happens. Here, we are providing the Matcher<Root> dependencies, the matchers, and all the other stuff.

In summary, Espresso is adding one kind of instrumentation. We are working with AndroidJUnitRunner which is included in the ATSL, where Espresso also uses a dependency injection framework. We’re using a BaseLayerComponent, providing two main modules: the BaseLayerModule and the UiControllerModule.

Synchronization (11:38)

One of the key features of Espresso is synchronization. When you are trying to do a view on the code, you know that the view that you are matching is on the screen, so you don’t have to take care of these things. Years ago, with other frameworks like Rhodium, you might have to do a wait until idle or even worse, try to make the thread sleep, trying to wait until the view is rendered. That’s the magic of Espresso.

Sadly, it’s not a perfect world, and we are not using Espresso in doing a simple onView. We’re using different libraries and architectures, and in this approach, we’re also using threads, AsyncTasks, RxJava services… and in this part, we are going to see more about that.

Performing a simple click (12:51)


// MainActivityTest
perform(click());

// ViewActions
new GeneralClickAction(Tap.SINGLE, VISIBLE_CENTER, Press.FINGER));

// Tapper
MotionEvents.sendDown(uiController, coordinates, precision);

// MotionEvents
uiController.injectMotionEvent(motionEvent);

How it works internally is that, when we are trying to perform a click as a view interaction, first of all in our MainActivityTest, we are performing the click itself, and we are going to the ViewActions. ViewActions is generating a general click action. Why general? Because maybe it’s not a single tap. Maybe you want to do a double click or click on the top left on the screen. That’s the class that handles everything about the clicks action.

Then, we are going to the Tapper, and we are sending an event, but here with the uiController. It is important here that we are using the uiController. And finally, in the MotionEvents class, we are injecting the motionEvent that in this case, it’s only a click.

Well, it’s a little bit hard to follow the track. And what’s the point of this, is it to use the uiController?

As we saw before, the uiController is the key to everything when we try to do something. Because again, I lock my activity, I don’t know what’s happening behind the scenes, and maybe I have to wait. Who is responsible to this idle state? The uiController. How does theuiController work?

Injecting the click (14:29)

First of all, I want to inject a click into the screen, right? So the first thing is looping the main thread until it’s idle. We’re working on the main thread because we are doing operations on the user interface, so we have to wait, or we have to know when we want to run this common onView, because maybe the screen is not ready. This loopMainThreadUntilIdle is taking a different state of different monitors, right?

Looping into an idle state… (15:00)

You have two main groups. One is the AsyncTask, and the other is the Idling, so the Custom Idling Resource. Espresso, at the beginning, is doing that isIdle, the queue of AsyncTask, because Espresso has one queue of message of AsyncTask. If it’s not ready, I want to send a signal with AsyncTask to say “loop until this message is okay.” The same for the idling resources.

Why AsyncTasks though? Well, we’re living in 2016, and although not everybody is using AsyncTask, internally, Android uses a lot of AsyncTasks. When you are typing something in any text in Android, when you type one character, it sends in one AsyncTask. The action to write is in the AsyncTask that Android is using. For this reason, we need a queue of AsyncTasks to know what’s happening behind the scenes.

How does Espresso populate this queue of AsyncTask? By reflection. At the beginning when we are instantiating the AsyncTaskPoolMonitor (that later we are going to see), it’s getting all the AsyncTasks pending on the message queue and creating the critical thread pool. The same for the idle resources. You know that you can create your custom idle resources. Here we are asking, is this idle resource really idle? Apparently not, so I send a signal and say look in the message queue until this idle resource is idle.

…and using the idle state (16:56)

This is a loop, and this happens until either everything is idle, or we’ve reached the limit of the timeout defined by Espresso. What happens afterward? Well, when we know that everything is not pending, so we have cleared the main thread, we can send or click to the main thread. But we are not sending that directly to the main thread. We are sending a signal with a flag saying that I want to execute this click event. And then we are calling the loop again until Espresso to executes the signal.

It seems to be quite complex, but it’s actually pretty easy. When I want to do an onClick, first of all, I have to clear the queue. Loop until the queue is cleared. After that, send a message saying that I want to do a click, and wait until the click is done.

Inner workings of the loop (18:10)


@Override
public void loopMainThreadUntilIdle() {
    do {
        EnumSet<IdleCondition> condChecks = EnumSet.noneOf(IdleCondition.class);
        if (!asyncTaskMonitor.isIdleNow()) {
            asyncTaskMonitor.notifyWhenIdle();
            condChecks.add(IdleCondition.ASYNC_TASKS_HAVE_IDLED);
        }
        if (!compatTaskMonitor.isIdleNow()) {
            compatTaskMonitor.notifyWhenIdle();
            condChecks.add(IdleCondition.COMPAT_TASKS_HAVE_IDLED);
        }
        if (!idlingResourceRegistry.allResourcesAreIdle()) {
            idlingResourceRegistry.notifyWhenAllResourcesAreIdle();
            condChecks.add(IdleCondition.DYNAMIC_TASKS_HAVE_IDLED);
        }
        loopUntil(condChecks);
    } while (!asyncTaskMonitor.isIdleNow() || !compatTaskMonitor.isIdleNow()()
        || !idlingResourceRegistry.allResourcesAreIdle());
}

Let’s see the code of the loopMainThreadUntilIdle. In this case, I removed some parts because they’re not important, but this is the real implementation of the code.

First of all, what we are asking is, is the AsyncTaskPoolMonitor idle? If it’s not, we are calling to notify when it’s idle. The same goes for all resources’ idle status. As you see in the code here, we have a loop until all the conditions are done, and meanwhile we are doing everything the same.

Two AsyncTaskPoolMonitors (18:52)

Well, what is the AsyncTaskPoolMonitor? We have not only one, but two AsyncTaskPoolMonitors in the code. Why do we have two? Because the AsyncTask between the compact version and the normal version are different. We use two implementations of AsyncTaskPoolMonitor to check what happened with the AsyncTask.

Pooling in Espresso (19:19)

Of course, we have some concepts like an AtomicInteger to know what’s happening, and a ThreadPoolExecutor. This thread pool is used by Espresso, using reflection, to populate the pending AsyncTask that you have in the queue in the main loop.

Checking for idleness (19:36)


boolean isIdleNow() {
    if (!pool.getQueue().isEmpty()) {
        return false;
    } else {
        int activeCount = pool.getActiveCount();
        if (0 != activeCount) {
            if (monitor.get() == null) {
                activeCount = activeCount - activeBarrierChecks.get();
            }
        }
        return 0 == activeCount;
    }
}

Quite easy, right? We have to check if the queue is empty for the pool, and then if not, we can define an interface call monitor notify when the resource is idle. This approach is used by the AsyncTask of the CompactAsyncTask. I also remember one year ago, I saw some developers using this approach to handle his own ExecutorService, for example.

Here you have your ExecutorService, so you are using the same approach. You are creating an AsyncTaskPoolMonitor but as the AsyncTask is only a pool, you can use your own ExecutorService and do the same. Maybe the bad thing here is that you’re adding a little bit of complexity, and it only works with your custom implementation.

IdlingResourceRegistry (21:09)


// IdlingResourceRegistry.java

public boolean registerResources(final List<? extends IdlingResource> resourceList {
}

public boolean unregisterResourcesfinal List<? extends IdlingResource> resourceList {
}

boolean allResourcesAreIdle() {
    for (int i = idleState.nextSetBit(0); i >= 0 && i < resources.size();
            i = idleState.nextSetBit(i + 1)) {
        idleState.set(i, resources.get(i).isIdleNow());
    }
    return idleState.cardinality() == resources.size();
}

The other thing that we are doing in loopMainThreadUntilIdle is maintaining the IdleResourceRegistry. Remember, when you are using one idle resource at the beginning of the code in Espresso, you must register and then unregister. That’s the official way Espresso works. When you’re trying to do an onView, or when you’re trying to do an onClick, first of all, we are taking the AsyncTaskPoolMonitor for the AsyncTask, the AsyncTaskPoolMonitor for the CompactAsyncTask, and the idle resource.

After that, we are sending the event of clicking. But again, that’s the perfect world with onView. How can we handle that with our code? We have an architecture where we are using a pool executor to execute our network calls, for example.

Guava’s WrappingExecutorService (21:53)

How can we handle this? Again, you can implement your idle resource. In our case we can use Guava. I saw that Guava has one class called the WrappingExecutorService, and it is trying to wrap with a delegate, ExecutorService. It’s quite interesting, because that means when you’re trying to execute the task, you can simply wrap this task and call the abstract method on the class.

Extending Guava (22:42)

The thing is, it’s private in Guava, but you can implement this abstract class in your project. So that means that I can create an IdlingResourceExecutorService extending from the Guava WrappingExecutorService, that I copied before in my code. Well, how does it work? Quite simply, I have two params: the ExecutorService, and the CountingIdlingResource, to help with knowing what is idle in the system.

Now you want to execute the runnable. You know that you can wrap this call and do the magic. First I increment in my idle resource, and when it’s finished, I decrement it. And now, I know that the system is idle. And that means that you can use your code, or because they are different approaches to the architecture, you can also use your own ThreadPoolExecutor in Espresso in this way, and it works very well.

RxJava and SchedulerHooks (24:24)


public class RxSchedulerHook extends RxJavaSchedulersHook {
    private Scheduler customScheduler;
    private CountingIdlingResource idling;
    private static RxSchedulerHook sInstance;

    public RxSchedulerHook(CountingIdlingResource countingIdlingResource) {
        FailedTest watcher = new FailedTest();
        idling = countingIdlingResource;
        customScheduler = new Scheduler() {
            @Override public Scheduler.Worker createWorker() {
                return new CustomWorker();
            }
        };
    }
    

Another case is where you’re working with RxJava. You are not going with ThreadPoolExecutor manually, and you link it in RxJava. Here we have different options, and I have seen in the past some options where you are wrapping the Observables. I don’t think that this works well, because maybe you have a hot Observable, and you want to know what happens not only ending with the onComplete.

The solution is to create an RxJava SchedulerHook that’s provided by the framework. That means that we can create a SchedulerHook, and it’s basically the same as the ExecutorService. Before the action is executed, I want to increment my atomic counter, and then I want to decrement.

Here, I’m also passing a CountingIdle, and then I’m creating a custom Scheduler. This is a custom Scheduler, after all, so it should be the same. You have to create a new worker of course, but you are doing the same magic. This mean that every time that you are executing something in RxJava, you are scheduling an action, you have the increment and the decrement, and it works.

Registering in RxJava2 (26:17)

That’s the solution that we are using at my company Agoda, because sadly we have a lot of RxJava. But it’s working very well, and it’s basically the same approach.

Wrap something like the ExecutorService of Java, but in this case, wrap the Scheduler, create a new worker, and increment-decrement, increment-decrement. In RxJava2, this is what the solution will be, where we can register a custom hook at the beginning of the project. When you are executing something in Espresso and RxJava, you are handling it this way.


RxJavaPlugins.getInstance().registerSchedulersHook(RxSchedulerHook.get());

OkHttp integration (26:50)

The next step is to learn about the RxJava ExecutorService. Now, maybe you don’t have a very complex application, and you are only doing calls with OkHttp, which is a call library that was released this year as well, and you still can do the same thing.

This library provides you with a way to wait until the call is done. I do a call with a dispatcher. We are registering the resource, and that’s it. Espresso executes the action when the call is finished. Here you see the internal code.


// OkHttp3IdlingResource

@Before
public void setUp() {
    OkHttpClient client = new OkHttpClient();
    IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", client);
    Espresso.registerIdlingResources(resource);
}

private OkHttp3IdlingResource(String name, Dispatcher dispatcher) {
    this.name = name;
    this.dispatcher = dispatcher;
    dispatcher.setIdleCallback(new Runnable() {
        public void run() {
            ResourceCallback callback = OkHttp3IdlingResource.this.callback;
            if(callback != null) {
                callback.onTransitionToIdle();
            }
        }
    });
}

public boolean isIdleNow() {
    return this.dispatcher.runningCallsCount() == 0;
}

It’s quite simple: we have a dispatcher from OkHttp, and you know that they have an idle callback in the implementation of OkHttp3. That’s how it’s working, right in the moment that the dispatcher is finishing the call, it’s calling the idle callback. So it’s just another solution.

All the complexity of the threading in Android can be handled with these three use cases. I forgot to mention one thing about this case though, and it’s that this solution doesn’t cover the debounce operators like delay, but in our case it’s not particularly important as it’s something more about the user interface that we are using.

After the threading synchronization with Espresso, we have to talk about rules.

Rules in Espresso (27:50)

If you’re working with Espresso, you are using activities with rules. In the previous instrumentation, remember that we had to use the ActivityInstrumentationTestCase2 extending from these classes, and it was a mess. Now it’s much easier, so we have to define this rule, ActivityTestRule, the class that we want to bind to, and that’s it.

In the moment that we are running our test, we know that the class Main is in on the script. This class provides some methods like beforeActivityLaunched. In some cases, we want to do operations here, and I want to lock something in before the activities launch. You can override it and do it. Besides, we have the two test rules as well: ActivityTestRule and IntentTestRule.

This is an extension of Espresso which tries to isolate the logic with older applications or activities. If I’m testing, for example, an application where when I click on one button, it triggers the dialer phone. I don’t want to test this application because it’s something about the system. I just want to test that intent. That’s the reason of the intent extension. The implementation is quite simple.

CustomActivityTestRules (29:45)

We can extend from the ActivityTestRule, and then we are only overriding two methods and in the tools in it, the intents. Everything’s in the intents. One optimization that we did in the company was merging both TestRules to create our custom TestRule. This means that if I want to stand something you create, I have to use IntentTestRule or ActivityTestRule. If you create that, you can obviously override the values, and do the same magic.

The third part in the ActivityTestRule is if the screen stopped, you have to do an interaction with the user interface 99% of the time. So you can override these params, and that’s it. That’s the first three created with ActivityTestRules.

It’s if you are taking the code, the extension on the extension, at the end you finish on the TestRule. It’s a concern, not for Espresso; but when working with JUnit, it’s a very common concept. It’s only a modification to how you are testing, how you are executing this. Let’s see examples. (SEE SLIDES)

TestRules on the UiThread (30:57)

One of them is this ActivityTestRule extending from UiThreadTestRule. It’s only an extension where we are overriding the UI thread statement to know that it’s in the main thread. So we are using a TestRule, but we’re overriding the value. How does it work for the activity, for example, and a statement? The statement is the thing that we are running in the JUnit, in the JUnit rule.

ActivityStatements (31:13)

Here mBase represents the test. It is the test that we want to execute. What is the ActivityStatement doing then? Before the mBase evaluates, that’s the thing of JUnit, we are launching the activity. And finally, we are finishing the activity. For my test, in my application, first of all, I want the activity, execute the test, and finally I finish the activity. That’s a statement from Espresso.

In cooperation with JUnit (32:11)

We are using concepts from JUnit, and of course, we can create our custom rules. In this case, I’m creating a new one that is the TraceTestRule, and before the evaluation of the test, I want to trace some metrics; and when it finishes, I want to trace, and report as well. How do I define this TestRule? Trace this rule and define it with the context in this case. That means that we create our own TestRules.

Using the TestWatcher (32:49)

Another important class from JUnit is TestWatcher; it’s an abstract class that tracks all the steps in a watcher. That means that you have a complete information about what’s happening when you’re trying to run a test. Here is the implementation of the TestWatcher. (SEE SLIDES)

We can see that before we start, we call the succeeded method. If there are errors, we’re calling the errors. We can create our own TestWatcher too. Imagine that I want to create the TestWatcher as a TestRule, that when I have one problem in my test, it takes a screenshot.

Creating an automated error screenshotter (33:54)


public class FailedTest extends TestWatcher {
    public FailedTest() {
        uiAutomation = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    }

    @Override
    protected void failed(Throwable e, Description description) {
        super.failed(e, description);
        String fileNameBase = getFileNameWithoutExtension(description);
        saveScreenshot(fileNameBase);
        saveInfo(fileNameBase);
    }

    @Override
    protected void succeeded(Description description) {
        super.suceeded(description);
    }
    
    @Override
    protected void skipped(AssumptionViolatedException e, Description description) {
        super.skipped(e, description);
    }

Here, I can create a TestWatcher and override the failed method, and take the screenshot with the UI automator. That means that you always have screenshots of your errors. But of course, you can override other methods if you want to do one screen set at the beginning. Again, TestWatcher implements the abstract class TestRule, and we can override the life cycle of the test.

Chaining rules together (34:40)


public RuleChain ruleChain = RuleChain.outerRule(new LoggingRule("outer rule")
    .around(new LoggingRule("middle around rule")
    .around(new LoggingRule("inner around rule))));
    

The next concept in the rules is the RuleChain. The RuleChain is only a sequence on how a sequence of TestRules is executed. The Java code of the TestRule, is quite simple. We have a RuleChain, and we have to define outer rules and inner rules, right? So that means that we can combine different TestRules in one.

If we see the output of this routine, the answer is this. (SEE SLIDE) First, we are executing the outer rule, then start in the middle, start in the inner, finish the inner rule (the third one), and finish in the middle rule. Finally, we finish the outer rule.

Let’s recap all the things about rules in one thing. First of all, we have seen that we have the CustomActivityTestRule, there is a merge of ActivityTestRule and IntentTestRule because we don’t want to use different TestRules. Then we have at the end seen our RuleChain.

Possible uses for the Chains (35:57)

This RuleChain can be used in this way. I had a failure test, remember? When I try to execute one test and it’s failing, I want to take a screenshot, and then I have my activity rule. That means that for every test in this TestRule, in this RuleChain, I’m taking a screenshot of the ActivityTestRule and so I have covered my problems in my test. But it’s not the only thing, because we can combine more activities and test rules in the same RuleChain. Remember that the trace that we defined at the beginning, creating our own TestRule. The same here, we are composing the routine with the FailedTest, the ActivityTestRule and finally the TraceTestRule.

We are using this approach, which is quite interesting because when you have an application with a lot of activities you can combine, and so you want to see some common cases in all the test.

Devices from AndroidTestRules (36:27)

Finally, with the rules, I want to talk about this library, it’s called AndroidTestRules. It’s from Shazam, and you can find custom TestRules to ignore tests, right?

Imagine that the typical thing that in this example that is provided by the repository I don’t want to test one test, or to execute one test if it’s Genymotion. You can do that with this library. But the most importantly, you are now using the definition of a TestRule.


public class Device {
    public Device() {
    }
    private static boolean deviceEquals(String device) {
        return Build.DEVICE.equals(device);
    }
    public static class Genymotion implements Condition {
        public Genymotion() {
        }
        public boolean isSatisfied() {
            return Device.deviceEquals("vbox86p");
        }
    }
}

If, for example, you are working with A/B testing, and you want to cover some case with Espresso, you can use this TestRule to say “only execute this test if it’s A or if it’s B.” The same if, for example, we are working now with a trunk approach so always, we are committing to develop, and we are working with features. Some features have to run the test, other features don’t have to run the UI test. We are using this library to avoid that.

Permissions (37:45)

In Espresso, you have to make a decision about whether or not you want to cover the permission.


@Before
public void grantPhonePermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        getInstrumentation().getUiAutomation().executeShellCommand(
            "pm grant " + getTargetContext().getPackageName()
            + " android.permission.CALL_PHONE");
    }
}

That’s one of the solutions implemented by Google in the repository of Android Blueprints. Before you execute your test, you send the pm grant permission message.

This works well if you have a small test. But in our case, we are using another implementation because we assume all the permissions are granted in our test. Because we are testing the regression - the full path of the application. We are not testing one isolated user interface.

Using Gradle for automated permissions (38:52)

It’s quite easy in Gradle. You hook the connectedAndroidTest (cAT) to execute the test, and create another task that’s granted permissions. You have a batch file in your project and you are granting all the permissions required by the phone. That’s the point with the permissions, I think that is quite simple, so we don’t have to think a lot about them.

Again, if you’re testing the what happen if the user accepts, or does not accept the permission; yes, you can test it, but if you are testing another thing like payment processes, those not involved with the permissions, you have to do that manually.

Agoda Stack (39:39)

I want to show the stack that is used in Agoda. We use ATSL and Spoon, I’m a very big fan of Spoon; Fork, that’s another cool library that you have to take into account; TestButler, which is from LinkedIn, it’s new, and it’s more interesting. Burst, RxPresso and AndroidTestRules are also used.

I’m also in Google Developer with Experts world, but in Agoda, we are looking for developers of Android, iOS, and API platforms. We are based in Thailand, so if you want to know more about how is life in Thailand, don’t be shy and we can speak - it’s an amazing adventure.

Q&A (40:29)

Q: Did you find out any problems while testing integration with Snackbar from software library?

Iñaki: Of course. All the things we created with Snackbar, Toast, dialogue, they are problematic. With progress bars, for example, in our flavour of instrumentation of Android, we are overriding the progress bar and using a custom view. Overriding the animation values for the internal pause, but even Google is not answering. They are saying that, “Hey, you have to test them this way; the other does not work.” With the progress bar, we simply override custom values of the progress bar in our instrumentation flavour.

Q: What’s your opinion of Appium versus Espresso, and why doesn’t Appium always support the latest version of Android?

Iñaki: Well, the main difference is that Appium is thought by QA departments. It’s thought by people who don’t know about the code. Espresso is targeted at developers, and they have a huge knowledge of the base code. They think that the instrumentation, the UI testing, is important.

It’s not the first company that I started working with Espresso after they left Appium. A QA department can work with Appium very well, but some moment, you have to speak with the developers. Imagine a complex situation such as A/B testing, where you need to have knowledge about the code to implement tests. With Espresso, using dependency injection, you can activate or deactivate values of the A/B testing, or the feature development branch.

About the Android versions for Appium: Well, they’re not official, right? May they’re a little bit behind, but I have to say that it’s a good product regardless.

Next Up: New Features in Realm Java

General link arrow white

About the content

This talk was delivered live in October 2016 at Mobilization. The video was transcribed by Realm and is published here with the permission of the conference organizers.

Iñaki Villar

Android Developer since 6 years ago. Iñaki worked with Bank apps in Spain for three years. Later he moved to Ireland to work with apps of airlines and GSM carriers. Iñaki is now living in Thailand working for Agoda. He’s a Google Developer Expert and enjoys being involved with the Android community.

4 design patterns for a RESTless mobile integration »

close