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

Friday, April 10, 2015

Encouraging the Next Generation



Recently, some of the people where I work have started a weekly writing prompt in order to get people to blog more often. This week's topic is “How do we involve the next generation of young minds?” and was inspired by two of my co-workers building quadcopters with a group of students in the northern part of the state.

This is a very open topic and very difficult to cover in a way that is less wordy than a copy or War and Peace (or at least your average work by Stephen King). However, I think it can be approached by breaking it down into a number of subjects which need to be addressed.

That being said, let's begin.


Start Them Early


The truth is that the earlier you start to instill curiosity and creativity in people, the easier it is to get them to continue on that path. There are a number of ways to do this, but if you're looking for ways to get a small child started, Legos, Lincoln Logs, Tinker Toys, etc are really a wonderful way to get the ball rolling. They offer nearly endless ways to combine pieces creatively, are tactile (which helps a lot of people, children or not. Never underestimate the value of physically building and handling something), and are generally fairly affordable.

Realize that not everything has to involve circuitry and code. Even things like basic woodworking projects can help instill creativity and the engineering mindset. I've built everything from birdhouses and toolboxes to actual buildings while I was growing up and I learned something valuable from every project.

As they get a little older, introduce them to things like crystal radio kits, 130-in-1 experiments kits (yes, they still make those), snap circuits and even simple programming languages like Scratch and Logo. A little later still get them involved in writing code on something like a RaspberryPi (it's fairly inexpensive and if you somehow manage to botch the os, just re-flash the SD card and start over. You can even extend it in order to interact with hardware). Tangoes are also a wonderful, inexpensive tool for teaching spatially related problem solving.

You want things that are simple to get a beginner's grasp of yet versatile enough to keep their attention after they master the basics and, most importantly, make them want to learn even more.


Mindset


A mind all logic is like a knife all blade. It makes the hand bleed that uses it.”
- Rabindranath Tagore

Logic is a very important part of being successful in technology fields. However, there is another part of the equation that is often overlooked – creativity. You really do need both the ability to think your way through a problem as well as the ability to look at a problem from a completely different angle than other people at times.

We need to foster not only logical thought, but also appreciation of and involvement in the arts. I would almost argue that the type of art doesn't matter nearly as much as being involved. Painting, drawing, writing, music, theater, and countless others – all of these things exercise your brain in ways that simple logic based problem solving doesn't.

On the logical side, we need to encourage the next generation of technical people to cultivate the skills to look at situations objectively, come up with a set of possible answers, and then evaluate or work through them (revising their general assumptions as they uncover more information) until they reach a conclusion. This is the time for measured experimentation instead of just wildly poking at a problem until something happens (though, admittedly, sometimes poking at the problem is necessary in order to uncover behavior).

Encourage the next generation to ask questions. That's not to say that you should spoon feed them the answers since guiding them through the discovery process is both an extremely effective way of teaching and often fun for everyone involved. However you do it, you should encourage them to ask the “whys” and “what ifs”.

Continuing in the vein of asking questions, foster the questioning of authority. If someone says “You can't do that” they should ask WHY. If it's because doing that thing is dangerous, that's one thing. If it's just because they don't think it can be done or because of other foolish reasons (“getting above your station”, “that's not something that proper girls/boys do”, etc), they should be encouraged to CHALLENGE IT.

We are, among other things, professional troublemakers. We create and change current reality as a part of our jobs. It's what we do. Innovation is inherently disruptive. Embrace that; don't try to stomp it out of the next generation.

Encourage reading. I can't stress this enough. Fiction, non fiction, philosophy – just read. It exposes you to different voices and approaches from your own. It's literally a different view on the world, and being exposed to that will help you grow.


Quick Feedback, Small Victories


Make initial victories easy to attain. This is especially true for younger children. Defeat is frequently demoralizing. Starting off with a victory encourages people to continue. It doesn't matter if it's just making a ball bounce on the screen. Give them something that provides near instant feedback that they have done something with a real, visible result.

As they progress, keep feedback loops tight even though the difficulty of what they are doing increases with time. Yes, I realize that this sounds a lot like Agile practices.


Make it Safe to Fail


I have not failed. I've just found 10,000 ways that won't work.”
- Thomas A. Edison

Failure, both professionally and personally, is an occasional fact of life. No matter what we try or how hard we work, there are times when things just won't happen the way we want them to.

The problem is that we have stigmatized failure to such a degree in this society that some people are afraid to even try because they might fail. This needs to stop.

Teach people that it's okay to fail on occasion. That's not to say that we should excuse laziness, but re-enforce the idea that failure occasionally happens, treat it as a learning experience, and teach others not to fear making a mistake.


Access to Tools


It may not occur to a lot of us in this field that not everyone is as well off as we are from an economic standpoint. Not everyone has ready access to the equipment needed to learn various technical skills. This is especially true in both poorer urban and rural areas.

Access to programing tools (IDEs, etc) has become considerably cheaper thanks to open source tools and educational/community licenses offered by companies like JetBrains and Microsoft. However, for some people, computers are still an expense that they can't justify (even if they are much cheaper than when I started learning to program).

That's not to mention the expense of things like quadcopter kits. Some of the things that you need access to for some projects are simply out of reach for a number of individuals as well as some school districts without outside help.


Access to Mentors


When I started out, I didn't have any mentors to help me learn how to code. The internet wasn't an option (yes, dinosaurs roamed the Earth and we had to walk to school uphill both ways). I had never even met or spoken to a professional software developer until I was in college. In fact, the only other people I knew who wrote code were a few friends in basically the same situation as myself.

It would have been much easier and a lot less discouraging if I had had access to mentors (even online) instead of having my only resources when I started out be the manual for a TRS-80 color computer and the occasional code sample in magazines borrowed from our very small public library (I told you that dinosaurs roamed the Earth at the time).

If you want to encourage the growth of a new generation of creative and technical people, you have to literally be there to encourage and guide them. Answer questions on the internet, make yourself available to schools/after school clubs and programs, and generally be a good community member.


Parental Involvement


This is the really difficult one. In order to encourage young people to go into technology based careers, their parents have to be positively involved. They can't just treat the computer as a way to babysit their child or, possibly even worse, view everything that their child does, no matter what it is, as simply “playing on the computer” like my parents did (which, I might add, included programming homework in college).

Keeping up momentum when it feels like nobody cares or you are being actively discouraged is extremely difficult. Not everyone is as hard headed as I am.

Parents don't even have to be experts in the field. They just have to be positive influences. Be curious about what your child is doing, encourage them, have them show off what they're doing to you a bit. Be a cheerleader. It's important.


Show Me The Money


Show people that there is a (generally) fun, well paying job doing work in this field and that it doesn't matter if they're a girl or a boy or even what socio-economic background they come from as long as they work at it. After all, it's important to be able to do things like pay your bills, go on vacation, and buy sandwiches.

As I said, this is by no means an easy problem, and this is only the short list of things that can be done to help, but it's a start and even if you can only manage a few of them that's better than the alternative.

Current mood: calm

Current music: Murray Head – One Night in Bangkok