Are you interested in adding Robolectric to your development process? Here’s a quick introduction to the most common types of test cases and some best practices to consider when deciding your testing strategy.
I’ve created two example apps for your reference. The first is a glorified Hello World and the other is a simple animation app. If you are unfamiliar with Robolectric, I’d start with my introductory blog that details how to integrate IntelliJ, Robolectric, and Maven into your project.
What To Test
You should focus your unit testing on business logic, basic view validation, and functionality. You can validate certain requirements very well with unit testing (e.g. “when I click this bell image, it whistles” or “all activities have a logo on the screen”).
Here are some common examples:
- Ensure the activity and view elements are not null.
- Ensure expected view elements are visibile on the screen.
- Ensure that the proper resources are used for view elements.
- Ensure items are hidden when expected.
- Ensure toasts are shown.
- Ensure that your intent starts the correct thing.
- Ensure that fragments are present.
What Not To Test
I don’t recommend investing much time in verifying less important styling details and XML attributes. Your user interface is likely to change many times a week, but typically the basic view elements and their behavior remain the same. If you updated a test each time a button had new padding, that would get very tedious indeed!
The app logo is 250dp high with a proportional width, the file name is “main_logo.png”, it has a 20dp left margin and 12dp right margin, has a content description of “Text,” and is centered vertically on every screen.
You are likely able to test all of the attributes above (I haven’t tried), but your time is best spent on items that are most important to the business. From this example, likely the most bang for your buck will come from testing that the proper image is used and making sure that it appears on all expected screens.
Returns start diminishing pretty quickly when you verify every possible XML attribute – unless that detail has an important business outcome for your app. Many of the attributes you don’t test can be caught by designers, quality assurance, business, product, and/or the engineers. A better stategy is to sweep the app near deployment with a designer to make sure the app appears as expected on a wide range of devices.
- Simplify your architecture. As with most object oriented realms, attempt to simplify the architecture as much as possible. Do this by creating abstract or base objects or interfaces. This way you can test shared behavior in one place instead of several, which simplifies tests and reduces code duplication.
- Mock/stub functionality. Often we want to ensure that a certain action was performed but not actually run the code. To do this, you create a test class which extends the class under test and override/mock out/track that a particular function was called. For example, if you want a particular click to make an API call, there is no need to make the actual call (since this would be an integration level test). You can use this strategy to track that the function that invokes the call has been called, but avoid making the actual call. This is also helpful when an operation crashes the unit testing framework (e.g. specialty library that causes funky side effects), doesn’t play nice, or is otherwise unnecessary to invoke directly.
- Create a custom test runner. Create a helper test runner to gives your entire unit test suite access to handy functionality. My simplest version includes string, drawable, and color resource fetching as well as view visibility helper functions. I also add a helper function to start fragments without a hosting activity. Finally, this is a good place to bind custom shadows you create and set up any external library configurations that you may need.
- Centralize test constants. Android strongly encourages the use of abstracting string resources into one place over using hard-coded string constants. Use a test constants class when you have lots of test data (e.g. When creating a user with lots of fields to mock out a JSON response).
- Have a robust testing strategy. Don’t rely solely on unit tests to ensure the proper implementation of your app. These tests, though quite useful, only give you a small window into your app and it’s behavior. At minimum, you should include integration level tests and have some level of manual QA in the process to ensure your app is everything that you want it to be.
- Unit testing Android apps is not as simple as with other platforms and Robolectric is still a work in progress. You will find things that you’d like to test that the library doesn’t suport. It’s up to your organization to determine how much they would like to invest to give back the the community by writing and fleshing out shadow classes.
- When running the sample tests you may notice a few warnings about binding map-related shadow classes (because the library is not available in the maven central repository). To solve this issue use the maven android SDK deployer.
Warning: an error occurred while binding shadow class: ShadowGeoPoint
Warning: an error occurred while binding shadow class: ShadowItemizedOverlay Warning: an error occurred while binding shadow class: ShadowMapController Warning: an error occurred while binding shadow class: ShadowMapActivity Warning: an error occurred while binding shadow class: ShadowMapView Warning: an error occurred while binding shadow class: ShadowOverlayItem
I hope you found this blog useful for getting started with Robolectric! If you are already using it, what do you find most useful or helpful? How does it fit into your testing strategy?