Adventures in Espresso and Unit Testing Part 1
17 Mar 2015 | by Carl AndersonNote: Espresso has moved to a new website, and thus some of the criticism below is out of date.
I recently decided it was time to add some automated testing to my app. After doing a bit of research, it seemed like adding Espresso for automated testing, and JUnit tests for my model objects would be the right way to go. So I started off by going to the Espresso setup page, which had a bunch of instructions for my IDE, Android Studio. It seemed relatively straightforward.
Setting up Espresso
The first thing you need to know with the Setup instructions page, is that it apparently has bugs in it that the maintainers are too lazy to fix. If you copy and paste everything, according to the instruction, the HelloWorldEspressoTest won’t compile or run. Notes from figuring this out:
- If you run proguard on your test binary (because you have lots of prolific APIs included), it’s not going to work. I had minifyEnabled set to true in my gradle build file, which prevented the tests from running. Fortunately for me I realized I didn’t need the 18k APIs that come with guava.
- Missing from the setup instructions is that you’ll need to have the full set of excludes under packagingOptions
- The example uses the @LargeTest annotation, but doesn’t say what it is, or why. I found a pretty good explanation here.
The BasicSample test they post has basically no explanation(which tends to be the status quo with this API). It wasn’t until using the API for a bit that I even understood what it tests - it looks for a EditText, types in a predefined string “Espress”, and performs a click on a buttons, and then tests whether another textView has the same text on it or not. You would think that check() would return a boolean value, but instead it returns a ViewInteraction.
Anyway, the entire test is fairly simple, and hopefully you can understand what it’s doing.
Test File locations
At this point I had changed my gradle file to have all of the changes necessary to get Espresso to run, the next step was writing an actual test. Two questions I had at this point - where do I put my tests, and how do I run them. Now, the guide suggests putting them in
src/androidTest/java/com.example.package/
This didn’t make sense to me. My source code structure uses a style like this:
src/androidTest/java/com/example/package/
There wasn’t any explanation explaining this difference (see the pattern?). Another thing that I really struggled with here, is that since I also wanted unit tests that ran separately, it wasn’t really clear what to do, and I ended up changing it around several times before getting it finally working. I finally decided to put all of my Espresso tests into:
src/androidTest/java/com/example/espresso
And I put my JUnit tests into:
src/test/java/com/example/model
Writing and running Espresso tests
Once I had all of that figured out, I wrote my first UI test for Espresso. It wasn’t much, I basically launched the legal notices activity for the app and did a check on whether it displayed the textview with all the legal notices or not. I found that none of the context menus within Android did things the way I wanted them to, and just ended up hand-coding the whole thing based on the Hello World example. It was fairly straightforeward. However, it was at this point where I really wanted some sort of “Espresso Manifesto” type document that spelled out things like:
- What is the goal / purpose Espresso?
- What are good things to test? What are bad things to test? What are some of the limitations?
- Why would I use Espresso?
The main Espresso page unfortunately doesn’t do a good job of answering these questions.
What I’ve learned since trying to do things in the framework is:
- Espresso is good at clicking on buttons, pretty good at putting text into boxes, and checking whether views are visible or not.
- Espresso is not good with Actionbar tabs.
- Espresso won’t help you make sure your layout isn’t screwed up. It programmatically looks up your view object, and as long as it’s on the screen everything will work.
- Sometimes things don’t work even though everything is on the screen. I ended up needing to use scrollTo() to get a button “visible”, even though as I was watching on the screen, it was fully visible.
So far I’ve had some decent success writing tests that navigate through my UI, but figuring out the process has been rather difficult. There’s just not very good documentation. For instance, when my button click was failing, because it wasn’t “90% visible”, I shouldn’t have to google a solution to this:
android.support.test.espresso.PerformException: Error performing 'single click' on view 'with id: test.com.myproject.app:id/navigationButtonProfile'. Caused by: java.lang.RuntimeException: Action will not be performed because the target view does not match one or more of the following constraints: at least 90 percent of the view's area is displayed to the user.
Another aspect that wasn’t really clear, is how to configure Android Studio to run the tests. This was more confusing once I added the JUnit tests, but suffice to say, the way I have it configured is as an Android Test (not a JUnit test) that runs all of the tests in a my specified espresso package.
Once I got through all of that, I was able to finally write a Test to go through a lot of different steps in my UI, which is cool and (hopefully) useful.
Part 2 can be found here
About the author
Carl Anderson is the lead Android developer for Trover.com’s Android app, having gotten his feet wet doing Android development at Amazon.com for the Kindle Fire. He has over 13 years of experience in software development, and has been writing Android code since 2013. He lives in Seattle with his wife and two daughters.