Friday, April 24, 2015

TDD in Android Part 1 – Getting Started




I've recently started learning how to write apps for the Android platform and, since the company I work for does all of our development in a Test Driven (TDD) fashion, I'm writing my first app using the same methodologies.

Unfortunately, the Android platform apparently wasn't designed with TDD in mind. Automated Unit and UI testing currently seem to be second class citizens because there isn't a lot of good information on testing in android. There are now decent tools, but not a lot of documentation on how to use them or avoid pitfalls.

I think it's time that we work to improve that shortcoming, so my plan is to walk you through the process of creating an app from scratch while pointing out the pitfalls and gotchas that I come across on the way so you will have a better chance of avoiding them. This entry represents the end result of my Sprint 0 (setup) phase for the app. It took about a week and a half of going through online documentation and talking with co-workers to get to this point. With this, you should be able to do it in an afternoon.


My App:
Pocket Bartender. This should be a relatively simple app since it's just going to be a fairly basic CRUD app. It will take a drink name or ingredient and give you a recipe or list of recipes for that drink or the drinks that use that ingredient. I'll also probably add functionality to favorite and hide drinks.

My Environment:
Macbook Pro
Phone: Samsung Galaxy s5 (sprint) running Android 5.0 (Lollipop)
Tablet: Samsung Galaxy Tab 4 (sprint) running Android 4.4.2 (KitKat)
IDE: Android Studio 1.0.1
Emulator (phone): Nexus 5 API 21 x86


Environment Setup


IDE:
Before you start, you'll need to have the Java 1.8 JDK installed and configured properly since coding for Android is done in Java (I am told that Groovy also works, but I haven't looked into seeing what's required to be able to code for Android in it).

Next, you'l want to install Android Studio. Since I was working through the Udacity Android course  before starting this project, I had Android Studio installed already.

Run the Android SDK Manager (Tools > Android > SDK Manager) and install the following packages:
API 19
Build-tools 21.1.2

These two packages will be required by the tools we'll install later.

Hardware:
Now that your IDE is installed, you'll need to enable developer mode on your phone and/or tablet (assuming that you want to test on hardware as well as in the emulator). How to do this may vary by phone maker/model, so I'll leave that portion of the exercise to you. It should just take a quick google search.

When you have enabled Developer Mode, go into Settings > Developer Options and make the following changes:

USB Debugging – ENABLED
Window Animation Scale – OFF
Transition Animation Scale – OFF
Animator Duration Scale – OFF

According to documentation, the last three settings can be manipulated via code, but it's easier to just turn them off in the Developer Options menu.

Open Android Studio and make a dummy project with a blank activity. We will be using this project later, so don't delete it after you're done with this step. When the project is created, follow the directions outlined in Step 3 under “Setting up a Device for Development” to set up your phone to be able to receive files from Studio. The steps required to do this vary by operating system.

When you plug your phone into your computer, it may not immediately show the dialog asking if you want to allow your phone to communicate via USB. If you don't see the window, don't worry. Just lock and unlock your phone a couple of times until it gives you the dialog.

You'll need to do this for each device you want to connect to your computer.

I've been told by one of my co-workers that his phone occasionally disables USB Debugging, so if you are having problems transferring your program from Android Studio to your phone or tablet, make sure the setting is enabled. Also, Android Studio occasionally fails to load the program properly, so you may just have to make sure the program is uninstalled on your phone and re-try (this happens to me on a somewhat regular basis).


Project Setup


Robolectric and Espresso:
While you can write unit tests for android in JUnit, the tests tend to run pretty slowly. Robolectric is considerably faster. Additionally, you can run UI tests using Espresso. Getting this set up manually is not a trivial task, but thankfully there is a project on github that will help alleviate a lot of the pain.

Download the Deckard project and follow the instructions on the page in order to get the project imported into Android Studio. The Deckard project comes with a Robolectric test file and an Espresso test file, both of which should run from the IDE as well as the command line. You may need to plug your phone into your computer in order to get the Espresso tests to run from the command line.

If you've ever done android development before, you will notice that the Deckard project is missing a lot of things. It is literally about as bare bones as a TDD android project can be and still function. We'll be fixing that later, but for now, what we have is fine.

Go ahead and change your package names to something that makes sense for your project and make sure the tests still pass. Just make sure that everything is in the same package.

At this point, the screen you get on your phone should look like this: 


Dagger:
Dagger is a dependency injection (DI) tool for android. According to all accounts, it is considerably faster than Roboguice (which was being used heavily before).

Setting up Dagger is fairly simple. Open your build.gradle file and, make it look like this version from my git repo.

Most of the changes are pretty self explanatory since they're just adding gradle entries for dagger and dagger-compiler. However, there are a couple of changes that require a little explanation.

compile 'com.squareup:javawriter:2.5.0' is used in order to resolve a dependency conflict between Dagger and Espresso. Both tools use javawriter, but they call for different versions. This, along with the exclude calls in dagger, dagger-compiler, and espresso-core forces the application to use a version that works with both.

At this point, most of the main tools for doing TDD in android are installed. We'll probably install Mockito for using mocks later, but we don't need it right now, so we'll do that when it comes up.


Hello World


Before we write our first code example of DI with Dagger, let's make a small change to the app in order to convince ourselves that it's really working (this will be the basis of the code we're injecting in a minute, so bear with me).

Open DeckardActivity.java and add the following lines to onCreate:

TextView t = new TextView(this);

t = (TextView)findViewById(R.id.text);
t.setText(“foo”);

Running your Espresso tests should now cause them to fail (that's a good sign in this case). Open DeckardEspressoTest.java and change the .check(matches()) statement to look for “foo” instead of “Hello Espresso”, run the tests to make sure they pass and then run the application in either your emulator or phone. The resulting screen should look like this:


Now that we know that we can change the message on the screen programmatically and the UI tests will still pass with the new string on the screen, we can write our first DI module. Make a Foo interface file as well as a FooObject class file with the contents of the files linked to on my github account. This is what we will be injecting.

You'll also need to make a module file in order to handle the actual injection. In this case, the file is DummyModule. Go ahead and copy the contents of the file from my github. Note that the injects line states the classes that the module will be injected into.

Open your DeckardApplication.java file and make the changes needed to make it look like the linked file on my github. At this point, I need to point out a couple of things that will save you future headaches.

First, while you can build the ObjectGraph by using DummyModule as an argument directly, I am told that you will want to build it using the getModules method for ease of testing later.


Second, in this example, getModules returns a List of type DummyModule. If you are using more than one module, it will return a List of type Object. Logically speaking, you should be able to make the example in this file return a List of type Object, but if you try, you get the incredibly weird error that your program can't cast type DummyModule as type Object (which makes no sense considering that, as a class, DummyModule inherits from Object). I have no idea why this is the case, but it is.

Now for the step that brings together everything we've done so far. Alter your DeckardActivity to look like the one linked to in my github repo. The changes from the last time that we touched the file (to make the screen read “foo”) are using @Inject to inject an instance of FooObject, to set a string equal to fooObject's provideText return value and to set the text on the screen equal to that string.

Your Espresso test should now be red. You'll need to change the DeckardEspressoTest.java file to check for “injected foo” instead of “foo” in order to make it green again.

Now, if everything works the way it should, your tests will pass and, when you run the program on either your phone or in the emulator, you should see the following: 


You're now set up to do TDD in android (minus the mocking tools, which we'll get to later). The program as it stands needs a great deal of work to get where we're going, but we're off to a good start.

In the next entry in this series, we'll start Sprint 1 tasks.

Current mood: Even
Current music: The Guess Who – No Time

No comments: