This is the second part of our series Growing Android Applications Guided by Tests. We’ve already covered The Setup in part one. Now we’re here to build a walking skeleton.
We start by writing a unit test for your scenario.
This Java object will represent a single flow of our application and will encapsulate the business rules behind this user story. Going test first allow us to discover the various dependencies that our scenario needs and to define the project structure. This package structure clearly reflects the intent of the application: all supported user stories sit on a package, and the boundaries should be as decoupled as possible from the rest of the application.
By defining the first scenario, “Search repositories by keyword”
(find code here) we set up the basic structure. But how can you start development? Should you start within the Database, or in implementing the Rest client or tweaking the Xml layouts? As many of you know, deciding where to start is an important step in Agile development, so important that an entire discipline has been created for this purpose.
BDD is at the core of the development here at Novoda. With the help of tools like Pivotal Tracker our clients can add and review their user stories, developers can monitor the state of the development and the QA can accept or reject the stories that have been implemented. This is a great way to promote transparency and keep the team focused for the current sprint, but there's more than that. Just as TDD is more than having your code under test, BDD is more than involving the clients in the development and to keep track of the current state of the application: it is a discipline that makes you write code in a particular way, focusing on what you want to implement, not how.
Let's make an example in our domain. Suppose that the first feature we want to include in our app is the ability to search for public repositories. A user story for this scenario may sound like: As a user, I should be able to search repositories by keyword and can be formally represented as follows:
@Given The github api, A user interface, A search request containing a keyword
@When The user triggers a search request through the ui
@Then The ui should display the github repositories retrieved for that request
The first thing that you'll notice if you're doing BDD is that every user story defined in a formal way can be written as a Java object:
@Given defines the constructor parameters,
@When is a method and
@Then is the behaviour executed in that method. Being a Java object means that we know where to start: we should write a test for it.
While writing this test we'll stumble upon the first objects and dependencies contained in our domain: the
Github Api Service, the
Presentation Layer, the
Response messages passed throughout the system and so on. And as soon as we make the test pass, we effectively implemented the business logic required to run that scenario.
Now look at the package structure. Usually, an Android project has a top level structure of activities, adapters, loaders, etc. To find out what that app does you have to dig deeper in the activities and fragments code and even then you don't have a clear picture of the intent of the developers because the business logic is spread throughout their architecture.
Compare this with our project: if you want to see what this application does, you simply open the scenario package and you should see a clear list of all the features that the current implementation supports. You may open the request and response packages and you'll know immediately what kind of messages are passed from the presentation layer to the boundaries and vice versa. This idea of making your architecture explicit has been know for a while in the developer community, but only gained momentum after a recent Uncle Bob talk. It can be summarised like that: databases, services and ui are not the important part of your application, they are just implementation details.
It may seem strange for an Android developer to think of Android as only a detail in its architectural design but the truth is, that this will lead to better a better de-coupled architecture. We should push Android to it's boundaries and retain as much as plain Java code as possible to keep objects easy to test. In principle, we should be able to run the very same application on a J2EE server, or on a Swing application. Of course you'll never do that, but that's the key to a clean, flexible and maintainable architecture, which is why you started doing TDD in the first place, right?
Add an end-to-end test for the first scenario
In order to actually explore the boundaries of our scenario, such as the UI interaction, we write a test that runs on a real device. This test will exercise the scenario code and the android frameworks, and will remain red until we have implemented the required UI elements.
Thus, in our presentation package we are forced to deal with android specific concepts like activities, adapters and managed lifecycle. We decide to use Robotium as our IT library as it make easier to write expressive and more robust tests, as independent as possible from the actual implementation.
Now that the business logic for our scenario is implemented, we can start exploring the boundaries and in particular the presentation layer that we will write for Android. We want to write a failing test that can accompany us until we can effectively show the result in our UI. We'll write a test that runs on a real device, so that we can simulate the UI events that our user will trigger in order to execute the search repositories scenario.
Integration tests are slow and brittle by nature, so we are not aiming for an high code coverage here. We just want our test to exercise the code in the scenario, so that we can be sure that all our objects interacts the way they should. You can use a code coverage plugin like Emma to verify that the code you want to test is effectively called.
You'll notice that in the test we are searching only for an handful of strings instead of checking a full response of 30-40 repositories. The reason is that Robotium
waitForText method is really slow, and you don't want those slow device tests to be even slower. Plus, the test resources that we are using should be kept in sync while we are expanding our model objects. All summed up, 3-4 objects are enough for this kind of test: remember that you'll probably want to run all tests each time that you want to commit to your central repository.