Over the last few years we have changed the structure of our Android consumer application from a large monolithic application to a modular one. This has numerous well-documented benefits such as being easier to maintain and scale, and improved build times.
One of the least documented benefits of modularity is the ability to create sample applications that just exercise a small piece of the main application functionality. This post will describe some of the benefits of ‘sample apps’ and also how you might set them up to help with your everyday development.
Benefits of a sample application
If the app that you work on everyday has a large codebase with a broad range of functionality, you will generally find yourself focusing on small sections of the app throughout your development time. These sections can be found at various points of the user journey. When manually testing, it can be quite painful to build, run, and then navigate to the area you want to test each time you make a change.
A modular application based on features makes it very easy to set up a sample app as part of your main project that launches straight into the area of functionality you are working on. This can save a lot of time when building, running, and testing changes compared to the main application.
There are other benefits to sample apps. The sample apps generally have a launch activity that has numerous fields so that you can launch your feature with a specific configuration. This can be as simple as an ID used for requesting some data from a server. For more complex features, each small piece of functionality can be enabled or disabled via a switch or field from the sample app launch screen.
You might be wondering how we wire the configuration of our sample app so that it runs the real feature code with specific data. I will describe that in the next section.
Sample application setup
The high level structure of a sample application is typically very simple, normally containing just a single launch activity. The activity often has a couple of responsibilities such as allowing the user to configure the data that is going to be used in the feature module and for us, it creates a Dagger component used for the DI in the module.
This gives developers a chance to configure the DI how they see fit when running their feature module code. For example, developers can configure the DI to temporarily divert crashdumps or log output to a temporary, non-prod destination while the module is under test.
The sample app modules would ideally just have minimal dependencies on other modules in your project. They would obviously require a dependency on the feature module they are running and also any additional modules specifically implemented to improve the usefulness of the sample app.
A sample app launch screen doesn’t have to be pretty, but it does have to be functional. It would generally navigate directly into the feature module by selecting one of possible multiple launch actions.
It might look something like this:
Enhancing your sample app
A few years ago, we made a concerted effort to further improve our UI tests. Looking to increase the coverage and also the reliability, we decided to switch the tests over to use mock data rather than hit the real API.
There are a number of different ways you can do this. After some discussion, we opted to use the OkHttp Mock Web Server and json files to model the data at the ‘lowest’ point. In order to easily increase our coverage, the json response files were ‘tokenised’ so that we could replace parts of the response at runtime during a test run.
We soon realised that as well as running our sample apps against real data, if we extracted the Mock Web Server functionality out into its own ‘fake-server’ module we could reuse it when running them in ‘mocked mode’ simply by adding it as a dependency to the sample application. This is useful for a number of reasons.
Firstly, you can launch your sample application without requiring any network connectivity. Secondly it means you can configure your sample app to launch in a number of different ways simply by setting values in the mocked json responses. This is helpful if you want to test the UI in different states.
Here’s a few examples of things you might want to configure:
- Add delays to responses to check progress UI
- Add long or short description fields to check for wrapping etc
- Configure HTTP errors to check error handling and UI
- You might have different entry points into your module providing different data. For example, you might allow deep links into your feature module but also have a more normal navigation route from another screen in your app. We navigate to each module via a Dispatcher object which might provide different data to the module depending on where we are navigating from.
- If you use feature flags, you might want to control switching them on and off from the sample app so you can quickly see their impact
- Simply return data in ‘optional’ json properties so that a particular feature fires
Another really valuable usage is if you’re developing a new feature module from scratch. If a contract with the server has been defined for the new feature but the API not implemented, the mobile developers can start implementing the feature whilst the backend developers are completing the code to provide the data. Meaning both sides can work in parallel with, ideally, just a small amount of integration work at the end of the feature development cycle.
Sample apps offer a number of benefits when developing large Android apps:
- They speed up development time by reducing the build time when implementing and manually testing new features
- If a mocking framework is incorporated into the sample app, both client and backend developers can work in parallel on new features
- The sample app can be made highly configurable in order to test UI scenarios rapidly without needing to find the correct backend data and navigate through the entire application
- Internal deployment of sample apps – so product, UX, or UI can review changes quickly and easily
Whilst there are a few challenges to creating sample apps within your main app project, such as having more build files to maintain, or that it can take longer to load the project in Android Studio. (This can be mitigated somewhat by temporarily unloading the sample apps you are not interested in). Overall I find sample apps provide a really great way to develop a large application efficiently.
OkHttp Mock Web Server – https://square.github.io/okhttp/4.x/mockwebserver/okhttp3.mockwebserver/-mock-web-server/
Android Modular Architecture – https://www.youtube.com/watch?v=PZBg5DIzNww