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:
Post a Comment