Sunday, December 29, 2013

Testing authenticated GAE endpoints in Android Studio

Preface

This post assumes that we can already test unauthenticated GAE endpoints as described in Testing and debugging GAE Endpoints in Android Studio.  There we used an Intel Atom (x86) emulator for better performance.
To test authenticated GAE endpoints, we need a valid Google account.  To add a Google account, we need an emulator with Google APIs.  Unfortunately, Google APIs are not available for x86-based emulators and we have to revert to slower ARM-based emulators.  As an alternative and for better performance, we can test on real devices as described in From an emulator to a real device.

Create Google account

This is the easiest part.
  1. On a device or an ARM-based emulator with Google APIs, open Settings application
  2. Scroll down to Accounts section
  3. Tap on Add account
  4. Select Google
  5. Follow the Add a Google Account wizard

Add authentication support to GAE endpoints

This is a two-step process that is applied to Project-AppEngine project and is described in detail here.
First, we need to specify authorized clients in the API backend. Second, we need to add a user parameter to methods for auth.  Following conventions used in the earlier post, we've just modified Project-AppEngine/src/main/java/com/example/project/TestEndpoint.java.
These steps turn on GAE endpoints authentication. Re-deploy the project and verify that authentication is enforced by the backend.
  1. Re-deploy Project-AppEngine: appengine:update
  2. Go to APIs explorer for your application
  3. Verify that un-authenticated requests fail
  4. Turn on Authorize requests using OAuth 2.0
  5. Verity than authenticated requests work
Now we can create client applications that communicate with authenticated GAE endpoints. Details for Android clients are described here.  Since our goal is to test authenticated GAE endpoints, we need to do more work.

Enable authenticated endpoints testing

Even though we will be running instrumentation tests deployed on the local dev server, it is still required to add an OAuth test client to our GAE application.  Local dev server will not accept connections from un-registered clients.  If we don't complete this and the following steps, we will get the error below:
com.google.android.gms.auth.GoogleAuthException: Unknown

Specify the test package name (optional)

In earlier posts we used com.example.package as an application package.  Based on that, the default package name for Project-endpoints/src/instrumentTest is com.example.package.test.  To specify different package name, modify Project-endpoints/src/build.gradle:
android {
    // ...
    defaultConfig {
        // ...
        testPackageName ’some.package.name’
    }
}
We will use default name com.example.package.test.

Add a test client 

  1. Go back and login to Google Cloud Console
  2. Click on the project you are working with
  3. Click on APIs & auth
  4. Click on Credentials
  5. In the OAuth section, click Create New Client ID button
  6. Under Application Type chose Installed application
  7. Under Installed Application Type chose Android
  8. Enter com.example.package.test into the Package name field
  9. Into Signing certificate fingerprint field, enter the SHA1 code that was used for the application package.
  10. Click Create Client ID button.
Copy Client ID of the new application, it will be used in the next step.

Add test Client ID to endpoint's @Api

  1. Go back to Project-AppEngine/src/main/java/com/example/project/TestEndpoint.java. 
  2. Add the test Client ID to the clientIds parameter of the @Api annotation.
The annotated TestEndpoint should look something like this:
@Api(
    name = “testendpoint",
    namespace = @ApiNamespace(ownerDomain = "com.example”, 
        ownerName = "com.example", packagePath = “package”),
    scopes = { Consts.EMAIL_SCOPE },
    clientIds = { Consts.WEB_CLIENT_ID, Consts.ANDROID_CLIENT_ID, 
        Consts.ANDROID_CLIENT_ID_TEST },
    audiences = { Consts.ANDROID_AUDIENCE }
)
public class TestEndpoint {
    // ...
}
where Consts.ANDROID_CLIENT_ID_TEST is the Client ID of the test application from the previous step.

Re-deploy the project

Since we will be testing authenticated GAE endpoints on local dev server, this step is not really required.  However, for consistency, it is still better to re-deploy the project.  Just run appengine:update maven task to update the server backend.

Modify project configuration

Modify Project-endpoints/build.gradle

dependencies {
    // ...
    instrumentTestCompile 'com.google.android.gms:play-services:4.0.30’
    instrumentTestCompile 'com.google.http-client:google-http-client-gson:1.17.0-rc'
}
Here we added dependency on Google play services library.

Modify Project-endpoints/src/main/AndroidManifest.xml

Add
<meta-data android:name="com.google.android.gms.version"
 android:value="@integer/google_play_services_version"/>
under the <application/> tag.

Clean and re-build the project

If we don't clean/re-build the project, we will be running into run-time errors described in an earlier post.
  • Build -> Clean Project or
  • Build -> Rebuild Project

Modify setUp() method

This is the last step that is required to enable testing authenticated GAE endpoints.  
  1. Go to Project-endpoints/src/instrumentTest/java/com/example/project/TestEndpointTest.java
  2. Go to TestEndpointTest.setUp() method.  This is the only method that needs to be modified.
  3. Replace the last null parameter of the Testendpoint.Builder() constructor invocation with a valid credential parameter.
The final TestEndpointTest.setUp() should look something like below:
@Override
protected void setUp() throws Exception {
    super.setUp();
    final GoogleAccountCredential credential = 
        GoogleAccountCredential.usingAudience(getContext(), 
            String.format("server:client_id:%s", WEB_CLIENT_ID));
    credential.setSelectedAccountName(ACCOUNT_NAME);
    final Testendpoint.Builder bld = new Testendpoint.Builder(
        AndroidHttp.newCompatibleTransport(), new GsonFactory(), credential);
    endpoint = bld.setRootUrl(DEV_SERVER).setGoogleClientRequestInitializer(
        new GoogleClientRequestInitializer() {
            @Override
            public void initialize(AbstractGoogleClientRequest request)
                throws IOException {
                request.setDisableGZipContent(true);
            }
        }
    ).build();
}
Modifications made in step #3 above are highlighted.  ACCOUNT_NAME parameter is the email of the Google account that was created the Create Google account step earlier.

Restart local dev server

  1. appengine:devserver_stop
  2. appengine:devserver

Run tests

At this point, if everything is configured correctly, we can run tests, debug tests and even debug GAE endpoints deployed on the local dev server as described in Testing and debugging GAE Endpoints in Android Studio.  The only difference from the previous post is now we are working with authenticated endpoints instead of un-authenticated endpoints.
If we set incorrect account name when calling credential.setSelectedAccountName() in TestEndpointTest.setUp() method, we will get the following error:
com.google.android.gms.auth.GoogleAuthException: BadUsername
If the account name is correct, the tests should run without problems.

No comments:

Post a Comment