Sunday, December 28, 2014

Espresso 2.0

It was almost a year since the previous Espresso release and finally Espresso 2.0 is released now. A good present before Christmas and New Year.

I'd like to avoid a deep dive in 2.0 features this time but focus on them rather in 2015 after having it a try.

For now take a look at Espresso Cheat Sheet - awesome overview of existing Espresso's features.

Enjoy your holidays and Happy New Year!

Tuesday, November 18, 2014

Catching CRASH or ANR of the Android app directly on your smartphone


In this post I just want to share with you my application which I'm using during daily testing activities and which helps me to have the CRASH stacktrace or ANR report immediately after it happens on your Android device - https://play.google.com/store/apps/details?id=com.error.hunter.

Please share your feedback regarding improvements if you'll have some :)

Update: I've uploaded this app source code to the github, so, you can also contribute if you want - https://github.com/denyszelenchuk/bug_radar.

Tuesday, October 28, 2014

Google Test Automation Conference - GTAC 2014

Hey. Don't miss GTAC 2014 https://developers.google.com/google-test-automation-conference/2014/ conference which is currently taking place in Kirkland.

Especially tomorrow talk from Espresso team. They plan to present something really big in regard of Android testing environment and also present the latest Espresso release.
Can't wait for it :)

You can watch live stream or view the recordings afterwards.

Wednesday, October 22, 2014

Updating Android SDK from command line

A good post about updating Android SDK from command line - http://tools.android.com/recent/updatingsdkfromcommand-line.

Very useful when you are running your tests in headless mode on virtual machine and you have to update SDKs from time to time.
That's it.

Wednesday, September 10, 2014

Discovering Espresso for Android: how to get current activity?

I see a lot of questions regarding getting current activity while testing with Espresso for Android across multiple activities. Below is the solution:
public Activity getActivityInstance(){
    getInstrumentation().runOnMainSync(new Runnable() {
        public void run() {
            Collection<Activity> resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED);
            for (Activity act: resumedActivities){
                Log.d("Your current activity: ", act.getClass().getName());
                currentActivity = act;
                break;
            }
        }
    });

    return currentActivity;
}
The thing is that getActivitiesInStage(Stage.RESUMED) returns us all activities in RESUMED state, and the activity which is currently displayed on screen, will be the first one in list.

Update for Espresso 2.0 - you have to add below imports and slightly modify your method:
import static android.support.test.runner.lifecycle.Stage.RESUMED;
import android.support.test.internal.runner.lifecycle.ActivityLifecycleMonitorRegistry;
public Activity getActivityInstance(){
    getInstrumentation().runOnMainSync(new Runnable() {
        public void run() {
            Collection resumedActivities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(RESUMED);
            if (resumedActivities.iterator().hasNext()){
                currentActivity = resumedActivities.iterator().next();
            }
        }
    });

    return currentActivity;
}

Thursday, June 26, 2014

Discovering Espresso for Android: implementing CountingIdlingResource

Hi, after a long pause I’d like to post an example how to use Espresso’s CountingIdlingResource using lazy getter and setter pattern.

So, at first, what is the  CountingIdlingResource?

CountingIdlingResource - an implementation of IdlingResource that determines idleness by maintaining an internal counter. When the counter is 0 - it is considered to be idle, when it is non-zero it is not idle. This is very similar to the way a java.util.concurrent.Semaphore behaves.

 The counter may be incremented or decremented from any thread. If it reaches an illogical state (like counter less than zero) it will throw an IllegalStateException. This class can then be used to wrap up operations that while in progress should block tests from accessing the UI.

At second, why do I need it? Espresso developers claim that using their test framework you can “Leave your waits, syncs, sleeps, and polls behind and let Espresso gracefully manipulate and assert on the application UI when it is at rest.” This is true, BUT if you are using AsyncTask while performing the network calls.

If  your application under test doesn’t use for some reason AsyncTask and uses ThreadPoolExecutor for network calls. Then this is your case.

It is really pretty easy to register CountingIdlingResource for this thread pool in your test package having getter and setter for the ThreadPoolExecutor instance in main app. The only requirement is to make this instance static to be able to set it from your test package. Below is an example how getter and setter look like in mine code:
public static ThreadPoolExecutor executor;

public void setExecutor(ThreadPoolExecutor executorNew){
    executor = executorNew;
}

public ThreadPoolExecutor getExecutor(){
    if (executor == null){
        executor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAX_POOL_SIZE, 1,
                    TimeUnit.MILLISECONDS, workingQueue);
    }
    return executor;
}
Then in your test package you can do the following :
  1. Create your custom CountingIdlingResource class which extends ThreadPoolExecutor and implements IdlingResource
  2. Override execute(…) method from ThreadPoolExecutor and increase the thread counter every time you hit it
  3. Override afterExecute(…) method to be able to decrease the thread counter when call finished and notify the Espresso when the thread counter is equal to zero that you are onTransitionToIdle state
  4. What I also find useful is to have method in this class that registers CountingIdlingResource for Espresso – registerCounterIdlingResources().
class EspressoCountingIdlingResource extends ThreadPoolExecutor implements IdlingResource {

    IdlingResource.ResourceCallback callback;
    private int threadCount = 0;
    static EspressoCountingIdlingResource idlingResource;
    static PriorityBlockingQueue workingQueue;
    static boolean flag = false;

    public EspressoCountingIdlingResource(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    @Override
    public synchronized void execute(Runnable r) {
        super.execute(r);
        threadCount++;
    }

    @Override
    protected synchronized void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        threadCount--;
        if (threadCount == 0) {
            if (null != callback) {
                callback.onTransitionToIdle();
            }
        }
    }

    @Override
    public String getName() {
        return "EspressoCountingIdlingResource";
    }

    @Override
    public boolean isIdleNow() {
        return threadCount == 0;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback callback) {
        this.callback = callback;
    }

    public static void registerCounterIdlingResources (){
        if (!flag){
            workingQueue = new PriorityBlockingQueue<>();
            EspressoCountingIdlingResource exec = new EspressoCountingIdlingResource(2, 3, 1, TimeUnit.MILLISECONDS, workingQueue);
            MyCustomPoolRequestHelper poolRequestHelper = new MyCustomPoolRequestHelper();
            poolRequestHelper.setExecutor(exec);
            registerIdlingResources(exec);
            flag= true;
        }
    }
}
After you made all these steps then registering of CountingIdlingResource looks really easy. Register it in your setUp() method like that:
EspressoCountingIdlingResource.registerCounterIdlingResources();
Oh, almost forget small but useful tip – I use the static boolean flag variable to check if  I’ve already register the same CountingIdlingResource for my test suite. If yes then I simply skip it the next time.
As you may know, Espresso can ignore registerIdlingResources() with the same names but in mine setup I don’t want to create once again instances of  MyCustomPoolRequestHelper which is different from that one which was registered at first step and can cause test failure.

Thanks for reading! And feel free to ask questions.

Friday, April 25, 2014

Testing that Android AlarmManager has an alarm set.

Just a small post from my recent experience - how to test that AlarmManager has an alarm set.

The first approach is to do it programmatically - let's assume we registered our alarm as below:
Intent intent = new Intent("com.my.package.MY_UNIQUE_ACTION");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, 
                                      intent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.MINUTE, 1);

AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 1000 * 60, pendingIntent); 
And now to check that registered above alarm is active we have to do the following:
boolean alarmUp = (PendingIntent.getBroadcast(context, 0, 
        new Intent("com.my.package.MY_UNIQUE_ACTION"), 
        PendingIntent.FLAG_NO_CREATE) != null);

if (alarmUp) {
    Log.d("myTag", "Alarm is already active");
} 
The idea is to use PendingIntent.FLAG_NO_CREATE which according to android documentation is a flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it (http://developer.android.com/reference/android/app/PendingIntent.html#FLAG_NO_CREATE).

The second solution is to use adb shell command:

After you run this command from the command line:
adb shell dumpsys alarm | grep com.my.package
you'll receive the list of active alarms for provided package name:
com.my.package +93ms running, 1 wakeups:
    +88ms 1 wakes 1 alarms: act=com.my.package.action.MY_UNIQUE_ACTION
cmp={com.my.package/com.my.package.AlarmReceiverService}  
More about dupmsys here - http://stackoverflow.com/questions/11201659/whats-android-adb-shell-dumpsys-tool-and-its-benefits.

Consider testing the alarm after:
  1. device restarted
  2. application was killed by the system or force stopped manually
  3. application upgrade

Happy testing!

Examples above were taken from this page - http://stackoverflow.com/questions/4556670/how-to-check-if-alarmmamager-already-has-an-alarm-set.

Monday, March 24, 2014

Discovering Espresso for Android: creating custom Matchers.

Hi! This time I'll talk about custom Matchers that can be used with Espresso for Android (and not only). We'll go through steps how to create them and I'll provide you examples of already existing and very useful ones.

First of all a couple of words about Hamcrest library, which provides us with common matchers and possibility to create custom matchers, to pay tribute to it's authors.

From Humcrest project main page - Hamcrest provides a library of matcher objects (also known as constraints or predicates) allowing 'match' rules to be defined declaratively, to be used in other frameworks. Typical scenarios include testing frameworks, mocking libraries and UI validation rules.

In one of my previous post I've described already how to use some custom Hamcrest matchers. The base idea is that matcher is initialized with the expected values, which are compared against the actual object we are matching when invoking it.

Among of the common matchers you can create your custom matchers using abstract class TypeSaveMatcher.class , with three methods to override:

public boolean matchesSafely(Fruit fruit) - matcher's logic,
public void describeTo(Description description) - description of the expected result,
protected void describeMismatchSafely(Fruit item, Description mismatchDescription) - description of actual value.

Example:
public static Matcher<Object> withItemText(String itemText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkArgument(!(itemText.equals(null)));
  return withItemText(equalTo(itemText));
}

public static Matcher<Object> withItemText(final Matcher<String> matcherText) { 
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkNotNull(matcherText);
  return new TypeSafeMatcher<Object>() {
    
    @Override
    public void describeTo(Description description) {
      description.appendText("expected text: " + matcherText);
    }

    @Override
    public void describeMismatchSafely(Object item description, Description mismatchDescription) {
      mismatchDescription.appendText("actual text: " + item.toString());
    }

    @Override
    public boolean matchesSafely(Object item) {
      return matcherText.equals(item);
    }
  };
} 
Here TypeSafeMatcher does the cast to a Object for us. The matchesSafely() method checks if the Object contains a text equal to itemText - and the describeTo() method produces a failure message when a test fails. Sometimes describeMismatchSafely() method is omitted.

Usage:
import static com.your.package.test.Matchers.withItemText;
... 
onData(withItemText("someText")).inAdapterView(withId(R.id.someId)).perform(click()); 
Esspresso introduced one more matcher type - BoundedMatcher<T,S extends T> - some matcher sugar that lets you create a matcher for a given type but only process items of a specific subtype of that matcher.

Example:
//Matcher for checking if EditText fields contain text hints
public static Matcher<View> withItemHint(String itemHintText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkArgument(!(itemHintText.equals(null)));
  return withItemHint(is(itemHintText));
}

public static Matcher<View> withItemHint(final Matcher<String> matcherText) {
  // use preconditions to fail fast when a test is creating an invalid matcher.
  checkNotNull(matcherText);
  return new BoundedMatcher<View, EditText>(EditText.class) {

    @Override
    public void describeTo(Description description) {
      description.appendText("with item hint: " + matcherText);
    }

    @Override
    protected boolean matchesSafely(EditText editTextField) {
      return matcherText.matches(editTextField.getHint().toString());
    }
  };
}

Usage:
import static com.your.package.test.Matchers.withItemHint;
... 
onView(withItemHint(editText.getHint().toString())).check(matches(isDisplayed())); 
How does it works - we go through all elements on the screen which are the subtypes of View.class, casting them to EditText.class, getting hint text with editTextField.getHint().toString() and matching to itemHintText.

And at the end some useful links:
https://github.com/yandex-qatools/matchers-java - Java library which implements hamcrest matchers for collections and webdriver webelements.
https://gist.github.com/frankiesardo/7490059 - custom matchers from Frankie Sardo for matching drawables.


Friday, January 31, 2014

Discovering Espresso for Android: swiping.


Hi, for today I have some tips about swiping ViewActions in Espresso for Android.

As you may know the latest Espresso release contains new swipeLeft and swipeRight ViewActions. They both are really useful when you'd like to swipe between activity fragments, tab layouts or any other UI elements.

You can use it as any other view action:

onView(withId(R.id.viewId)).perform(swipeRight());

But be aware that doing this you will operate on a view, in our case R.id.viewId, but not on the screen size. That means that to swipe right or left, for example between fragments you have to deal with some parent layout or maybe list view.

If you take a look inside Espresso's ViewActions.java class you will see below code for swipeRight() method:

  public static ViewAction swipeRight() {
    return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.CENTER_LEFT,
        GeneralLocation.CENTER_RIGHT, Press.FINGER);
  }

As you may guess GeneralLocation.CENTER_LEFT and GeneralLocation.CENTER_RIGHT are the from and to coordinates accordingly inside the view. The full positions list which can be used as from and to coordinates are: TOP_LEFT, TOP_CENTER, TOP_RIGHT, CENTER_LEFT, CENTER, CENTER_RIGHT, BOTTOM_LEFT, BOTTOM_CENTER, BOTTOM_RIGHT.

For other two parameters' names Swipe.FAST and Press.FINGER it's clear without saying what each of them means. For now Swipe has only FAST (100 milliseconds) and SLOW (1500 milliseconds) swipe speeds, and Press has PINPOINT (1x1 mm), FINGER (16x16 mm) and THUMB (25x25 mm) press areas.

Of course there is always an option to implement your own speed and press area - thanks for Espresso team for making the source code open.

Seems to be clear enough, right?

And the last thing - implementing our own swipe down and up ViewActions which can be used to initiate swipe down to refresh action or to perform swipe up (going to the bottom of the view), since they are not yet included into Espresso's code:

public static ViewAction swipeDown() {
    return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.TOP_CENTER,
        GeneralLocation.BOTTOM_CENTER, Press.FINGER);
}

public static ViewAction swipeUp() {
    return new GeneralSwipeAction(Swipe.FAST, GeneralLocation.BOTTOM_CENTER,
        GeneralLocation.TOP_CENTER, Press.FINGER);
}

Thanks for reading!

Friday, January 10, 2014

Espresso for Android 1.1 released!

Google Espresso Team just announced about the second 1.1 release of their small in size but huge in potential baby. The new features are:
  1. so expected swiping ViewActions - swipeRight() and swipeLeft()
  2. maybe even more desired multi-window support
  3. new type text ViewAction - typeTextIntoFocusedView(String stringToBeTyped)
  4. bugfixes and improvements
The release notes available at android-test-kit page - release notes Espresso 1.1.

I'd also like to mention useful features that are not present at release notes page:
  1. added possibility to create custom Root matchers
  2. updated scrollTo() ViewAction for horizontal scroll view support
  3. added NoActivityResumedException which indicates that there are no activities in stage RESUMED
  4. added DrawerActions and DrawerMatchers
  5. and last but not least - new stylish logo
Stay tuned!

Monday, January 6, 2014

Discovering Espresso for Android: matching and asserting view with text.

After more than a month of using great test tool from Google - Espresso for Android, I'd like to share with you some of my experience. I assume that you've already added espresso jar into your project, spent some time playing with Espresso samples and have basic understanding how this tool works.

In this post I'll show how to match particular view with text or assert that it contains (or not) specified Strings. Before we start, you have to take a look at Hamcrest matchers - Hamcrest tutorial and API Reference Documentation, which are used together with Espresso's ViewAssertions and ViewMatchers and included into Espresso standalone library. Pay more attention to Matcher<java.lang.String> matchers.

So, here we go. For simplicity following String "XXYYZZ" will be used as a expected text pattern.
Espresso ViewMatchers class implements two String matcher methods withText() and withContentDescription() which will match a view which text is equal to specified expectedText or specified expectedContentDescriptionText parameter passed into it.
onView(withText("XXYYZZ")).perform(click());
onView(withContentDescription("XXYYZZ")).perform(click()); 

 

Using Hamcrest String matchers we can create more flexible matcher combinations:

 - matching a view which text starts with "XXYY" pattern:
onView(withText(startsWith("XXYY"))).perform(click());
 - matching a view which text ends with "YYZZ" pattern:
onView(withText(endsWith("YYZZ"))).perform(click());
 - assert that text of a particular view with specified R.id has content description which contains "YYZZ" string anywhere:
onView(withId(R.id.viewId)).check(matches(withContentDescription(containsString("YYZZ"))));
 - matching a view which text is equal to the specified string, ignoring case:
onView(withText(equalToIgnoringCase("xxYY"))).perform(click());
 - matching a view which text is equal to the specified text when whitespace differences are (mostly) ignored:
onView(withText(equalToIgnoringWhiteSpace("XX YY ZZ"))).perform(click());
 - assert that text of a particular view with specified R.id does not contain "YYZZ" string:
onView(withId(R.id.viewId)).check(matches(withText(not(containsString("YYZZ")))));

 

 Adding allOf() or anyOf() Hamcrest Core matchers gives us even more power:

 - assert that text of a particular view with specified R.id doesn't start with "ZZ" string and contains "YYZZ" string anywhere:
onView(withId(R.id.viewId))
    .check(matches(allOf(withText(not(startsWith("ZZ"))), withText(containsString("YYZZ")))));
- assert that text of a particular view with specified R.id ends with "ZZ" string or contains "YYZZ" string anywhere:
onView(withId(R.id.viewId))
    .check(matches(anyOf(withText(endsWith("ZZ")), withText(containsString("YYZZ")))));
The last note - to use all of mentioned above ViewAssertions, ViewMatchers and Hamcrest matchers just add below imports:
import static com.google.android.apps.common.testing.ui.espresso.Espresso.onView;
import static com.google.android.apps.common.testing.ui.espresso.assertion.ViewAssertions.matches;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withId;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withText;
import static com.google.android.apps.common.testing.ui.espresso.matcher.ViewMatchers.withContentDescription;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.startsWith;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.equalToIgnoringCase;
import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace;
So, basically it. If you stuck at some point and don't have an idea how to proceed, you can ask a question in androit-test-kit Google group. You'll probably get an answer from co-author of Espresso tool - +Valera Zakharov or other experienced users.

Thanks for reading!