Wednesday, April 29, 2015

Google Hangout on Air from Google+ Pages

For the GDG Boulder meetup this Thursday, I would like to broadcast a Google Hangout on Air from the GDG Boulder G+ Page so that the recorded video will be saved to the GDG Boulder YouTube account. Took me a while to figure out the steps, so I want to document them here.

Prerequisite: You need to have a YouTube channel already linked to your Google+ page.

Switch to the Google+ Page

On Google+, click on your photo on the top right corner and switch to use Google+ as the Page admin.

Select "Hangouts" on the drop-down

Hover on the drop-down on the left, select "Hangouts".

Select "Hangout on Air"

Now you are on the Hangouts page as the Page admin. Select "Hangouts on Air" from the top tabs and you can now create a Hangout on Air as the Page admin!

Here is the Hangout on Air I created: https://plus.google.com/events/cgrute825m3sv909nd0da0if0ag

After you created your Hangout on Air, don't forget to enable the Q&A app. You won't be able to do that after the Hangout started.

Happy broadcasting!

Saturday, April 25, 2015

Espresso: Custom Idling Resource

One key feature in Espresso is the synchronization between the test operations and the application being tested. This is built around the concept of idling: Espresso waits until the app is "idle" before performing the next action and checking the next assertion.

Idle

What does it mean for the app to be idle? Espresso checks for several conditions:

  • There is no UI events in the current message queue
  • There is no tasks in the default AsyncTask thread pool

This takes care of waiting for the UI rendering and AsyncTask completion. However, if your app performs long-running operations in other ways, Espresso will not know how to wait for those operations to finish. If that is the case, you can tell Espresso to wait by writing a custom IdlingResource.

IntentServiceIdlingResource

Say you use an IntentService to do some long computation, and return the result to your activity via broadcast. We want Espresso to wait until the result is returned before checking that it was displayed correctly.

To implement an IdlingResource, you need to override 3 functions: getName(), registerIdleTransitionCallback() and isIdleNow().

@Override
public String getName() {
  return IntentServiceIdlingResource.class.getName();
}

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

@Override
public boolean isIdleNow() {
  boolean idle = !isIntentServiceRunning();
  if (idle && resourceCallback != null) {
    resourceCallback.onTransitionToIdle();
  }
  return idle;
}

private boolean isIntentServiceRunning() {
  ActivityManager manager = 
    (ActivityManager) context.getSystemService(
      Context.ACTIVITY_SERVICE);
  for (ActivityManager.RunningServiceInfo info : 
          manager.getRunningServices(Integer.MAX_VALUE)) {
    if (RepeatService.class.getName().equals(
          info.service.getClassName())) {
      return true;
    }
  }
  return false;
}

The idle logic is implemented in isIdleNow(). In our case, we check if our IntentService is running by querying the ActivityManager. If the IntentService is not running, we can inform Espresso calling resourceCallback.onTransitionToIdle().

Register your idling resource

You need to register your custom idling resource in order for Espresso to wait for it. Do it in a @Before method in your test, and unregister it in @After.

@Before
public void registerIntentServiceIdlingResource() {
  Instrumentation instrumentation 
    = InstrumentationRegistry.getInstrumentation();
  idlingResource = new IntentServiceIdlingResource(
    instrumentation.getTargetContext());
  Espresso.registerIdlingResources(idlingResource);
}

@After
public void unregisterIntentServiceIdlingResource() {
  Espresso.unregisterIdlingResources(idlingResource);
}

Full example

Check out the source code for a full example. Try commenting out the IdlingResource registration and watch the test fail.

Source code: https://github.com/chiuki/espresso-samples/ under idling-resource-intent-service

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

Tuesday, April 21, 2015

Espresso 2.1: ActivityTestRule

When I wrote my android-test-demo app, I wanted to use Jake Wharton's ActivityRule, but couldn't because I wanted to customize the launch intent for each test method.

Today I got my solution from Google: Test rules in Espresso 2.1. ActivityTestRule can be configured to take a different launch intent per test method like this:

@Rule
public ActivityTestRule activityRule = new ActivityTestRule<>(
    MainActivity.class,
    true,    // initialTouchMode
    false);  // launchActivity. False to set intent per method

I then supply a launch intent in the test method:

@Test
public void intent() {
  DateTime dateTime = new DateTime(2014, 10, 15, 0, 0, 0);
  Intent intent = new Intent();
  intent.putExtra(MainActivity.KEY_MILLIS, dateTime.getMillis());

  activityRule.launchActivity(intent);

  onView(withId(R.id.date))
      .check(matches(withText("2014-10-15")));
}

See MainActivityTest.java for more details.

ActivityTestRule makes tests much cleaner. Thank you Google for making Espresso better and better!

Like this article? Take a look at the outline of my Espresso book and fill in this form to push me to write it! Also check out the published courses: https://gumroad.com/chiuki

Saturday, April 18, 2015

Espresso book?

I have been using Espresso for more than a year now, and I love it. I am considering writing a book / recording a video class / giving talks & workshops on the topic, but it is a substantial amount of work so I want to gauge interest before I start. If this material is valuable to you, please let me know by filling this form.

The outline

Introduction

  • What is instrumentation testing?
  • What is Espresso?
  • Basic anatomy of an Espresso test

ViewMatchers

  • How do matchers work?
  • Combining matchers (Hamcrest & Espresso)
  • Matching inside an AdapterView
    • onData
    • onChildView
    • inAdapterView
  • Using RecyclerViewActions with RecyclerView
  • Custom matchers

ViewActions

  • click(): What happens behind the scene?
  • Custom actions
    • getConstraints()
    • perform()

ViewAssertions

  • matches(ViewMatcher)
  • doesNotExist()
  • Custom assertions

Intents

  • Incoming: setActivityIntent
  • Outgoing: ActivityMonitor

App synchronization

  • The notion of idling
  • Custom IdlingResource

Repeatable tests

  • Avoid external dependencies (network, device configuration etc)
  • Dagger and Mockito

Cast studies

  • Combining various techniques in real-world scenarios

Your input

As a start, I submitted a talk proposal to Droidcon NYC. Please fav this tweet if you want to hear it:

However, the talk is only 40-minute long, highlighting maybe a quarter of this outline. It will take a lot more effort to flesh out the whole outline, so please let me know your interest by filling in this form: http://bit.ly/1H0X4up. Thank you!

Update: I have started publishing Espresso courses! https://gumroad.com/chiuki. Still trying to figure out what topics to tackle next, so I'd really appreciate your input.

Thursday, April 9, 2015

Dagger 2 + Espresso 2 + Mockito

I've been doing Android instrumentation testing with Dagger, Espresso and Mockito, and I love it. To commemorate the launch of Dagger 2 out of SNAPSHOT, I am sharing a demo repo with Dagger 2, Espresso 2 and Mockito:

https://github.com/chiuki/android-test-demo

Dagger Components

Dependency injection allows our app and test obtain different modules. This is very useful for creating repeatable test cases. The demo app displays today's date in the format yyyy-MM-dd. We would like to test that against a known date, instead of depend on the actual date when we run the test.

In Dagger 2, a Component provides modules for your whole app, and defines where to inject them.

public interface DemoComponent {
  void inject(MainActivity mainActivity);
}
@Singleton
@Component(modules = ClockModule.class)
public interface ApplicationComponent extends DemoComponent {
}
@Singleton
@Component(modules = MockClockModule.class)
public interface TestComponent extends DemoComponent {
  void inject(MainActivityTest mainActivityTest);
}

ApplicationComponent is used when the app is run normally, and TestComponent is used during tests. Both components injects into MainActivity.

How does the MainActivity know which component to use? It injects via the application, which stores the component.

Approach 1: setComponent

private DemoComponent component = null;

@Override public void onCreate() {
  super.onCreate();
  if (component == null) {
    component = DaggerDemoApplication_ApplicationComponent
        .builder()
        .clockModule(new ClockModule())
        .build();
  }
}

public void setComponent(DemoComponent component) {
  this.component = component;
}

public DemoComponent component() {
  return component;
}

We call setComponent() in test, which runs before onCreate(), so the TestComponent is used. When the app is run normally, component will be set to ApplicationComponent in onCreate().

Approach 2: Mock application

Exposing setComponent in the application is not great because ideally the application should not have test-specific code. Another approach is to subclass the application in the androidTest folder and load it during tests via a custom test runner. See blog.sqisland.com/2015/12/mock-application-in-espresso.html for more details.

Mockito

The app has a Clock class which returns the current time.

public DateTime getNow() {
  return new DateTime();
}

TestComponent contains MockClockModule, which provides Clock as mocked by Mockito. This way MainActivityTest can supply a pre-determined date during test.

Mockito.when(clock.getNow())
  .thenReturn(new DateTime(2008, 9, 23, 0, 0, 0));

Since we have singleton modules, the same mocked Clock is supplied to the app. With that, it will display the provided date instead of today's date:

onView(withId(R.id.date))
  .check(matches(withText("2008-09-23")));

More

There is a lot more in the repo, including testing activity launch with intent and unit testing with JUnit. Please check it out:

https://github.com/chiuki/android-test-demo

Look at the setComponent branch to see the old code for injection using the setComponent function. Master uses mock application and custom test runner for injection.

Also, I am toying with the idea of writing a book on Espresso. Please take a look at the outline and fill in this form if you think I should write it!

Update: I have started publishing Espresso courses! https://gumroad.com/chiuki

Further reading: