It had to happen eventually. I will move the project to use Gradle as I should from the first place. I feel that the Android Gradle plugin is not mature enough and doesn’t have enough online presence (meaning not enough information in StackOverflow). But even so, now that Android Studio is officially out, there is not way around this. I will need to get familiar with Gradle and Groovy at some point and what better time than the present. So here we go.
So I have a simple notes list activity with a notes list fragment in it. When starting to test the activity, all went well. But when I created the actual fragment and started implementing it, I encountered a problem. If this fragment has an external data source to get the data, do I really need to mock that data source to make my test run fast? if so, what am I testing? Clearly to test the Activity I need to mock the fragments that it contains. To do so, I need to create mock XML files and set them in the configuration of the test. Hope this all goes well. I will post this when I’m done. Robolectric supports alternative resources for testing and it would be the first time I try this feature. Wish me luck.
I think I cheated a little bit. I knew that my notes list activity will need to support both tablets and phones, so I started writing tests based on the Android master/details template. I guess this is not exactly what I should have done. I just needed to create the list fragment inside the notes activity and implement that (again using TDD). But never mind, I did write the tests first though.
Doing things this way is strange. The most noticeable thing is that I didn’t use my emulator till now. I created empty Activities with empty fragments in them. I still didn’t implement the list fragment and I only created it to pass the notes activity tests. Writing tests to UI classes that are not yet visible is a fun experience and refreshing in client side code. It does feel slower, but I enjoy a comprehensive test suite that covers all of my current functionality and that is great.
The main challenge is not to jump ahead and implement things that are not needed for the test to pass. That is always a problem when you are not experienced in TDD, but I think it is even more of an issue when you need to do it on Android. Android is not very test friendly and using Robolectric takes time to get used to. I am much more comfortable with writing Android code without tests, and only test POJOs. Creating tests to my Activities and Fragments feels a bit like developing with one hand tied behind my back. But I hope that in time I will gain more experience in applying TDD on Android using Robolectric and this will feel like TDD should feel like (whatever the hell that means…).
You can see all my commits in the repo (link on the right hand side). The branch is called feature/notes-list (will not exist once I merge this to develop, but you will be able to find the merge commit).
That’s it for now. Will update soon.
This question arose when discussing the use of Robolectric. One of my colleagues told me that they stopped using it for unit testing at some point but kept it for very small set of integration testing (but they more or less stopped using it). So why is that? from the same reasons I pointed in the previous post – modular, testable code (no mix of logic and UI). As I also said in the previous post, you sometimes start from the UI and implement the app Top Down and Not Bottom Up (top being UI of course). Then I started thinking on my little project. How should I start? I decided to start from the bottom up creating the Note model first. But now that I started thinking about it, I think I made a mistake going down that path. Why? this is a mobile application. The UI is all that matters eventually. All the rest comes to cater the UI in some way. I can improve the backend of the app as much as I want but I should start from a basic functioning UI. I will make this UI with the simplest backend possible: just a list of strings as the back end, and see where it takes me. Considering myself as less a UI kind of guy (or to quote one of the designers I worked with – “don’t quite your day job to become a designer”), then I think starting this way will remind me what is the ultimate goal here. Hope this bet will pay off in the end.
Well, let’s write some code.
Since I wanted to do TDD in Android, the first stop was the Android Instrumentation framework. One of the main disadvantages of this framework is that every test must be deployed on the device. One of the important elements that Uncle Bob in his TDD workshop talked about was the speed of the cycle – write a fail test -> make the test pass -> refactor. If every time you are running the unit tests you need to wait till the app is packaged, uploaded to the device/emulator and then run, this will make the whole cycle tedious leading most people to avoid the tests. So Instrumentation seemed far from ideal.
Then, I came across Robolectric. This open source project basically allows you to run your test on your machine JVM without needing the device. This looked very promising. Since I have not used Gradle as of yet, I looked and found an Ant example of using Robolectric. This was exactly what I was looking for. But a discussion with my team lead about this framework lead me to rethink the whole thing. Maybe your Activity/Fragment/Application classes shouldn’t contain that much logic in the first place. They should just delegate state from other POJOs that can be easily tested using plain old JUnit. In reality, many people for most apps put quite a lot of logic in their activities. Abstracting this out to a separate object might over complicate your code which is doing quite simple functionality. Also, you don’t want to jump immediately to this design. You can start by putting most of your code in the Activity and little by little separate it and refactor it. Doing this in advance because you lack the tools to do so is not ideal and actually defies the whole cycle of the TDD methodology.
So I created the project with Robolectric in it. Time will tell how this will play out.
Here goes nothing.