I’m a proponent of unit testing and once you figure out how to use Robolectric its great for helping you unit test Android. That is, until you run into an instance where a Shadow class is not implemented and you get some obscure error while running the unit test. Such thing happened recently when trying to get some code under test.
The code previously used a Context#startActivitiy(…) to start the Activity. I’m building some deep linking framework for a client for their Android app and I needed to use the TaskStackBuilder in order to build the back tasks properly. The code looked similar to this:
The code above starts and Activity and if the Activity defines a parent activity in the AndroidManifest.xml the entire task stack will be built and the activity will be started. My existing unit test to test this looked like this:
The goal was to make sure the test above still passed when I moved from the traditional Context object to a TaskStackBuilder instance for starting the Activities. Unfortunately moving to use the TaskStackBuilder made the test fail with a framework exception. Robolectric does not have a ShadowTaskStackBuilder. So I built a very rudimentary one that would help my test pass, its not 100% perfect by any means but it should give you a jumping off point if you want to extend it as it needs a bit of work.
Writing the Shadow
When the test is run in Robolectric and configured to use this Shadow (as shown below) the code above will set up a new instance of TaskStackBuilder and use the Shadow to manage as the execution. In What we’re actually doing is avoiding all of the actual Implementation of TaskStackBuilder all together so we can get this test to pass. The code builds a TaskStackBuilder and then when the addNextIntentWithParentStack(Intent intent) method is called the intent is saved locally in the shadow. When the user calls startActivities on the TaskStackBuilder, we intercept that and simply call context.startActivity(intent). Since the context is an instance of Robolectric.application this works out great.
Is this perfect? By all means, no. But it will help someone who is stuck. How can it be improved? If you look at the source of TaskStackBuilder you’ll notice it delegates the activity creation and starting of them to a class called ContextCompat and it also uses IntentCompat. The source of ContextCompat reveals the usage of two other classes: ContextCompatJellybean and ContextCompatHoneycomb as well as some other code. As you can see, its a mini pandoras box under the hood. My implementation above avoids all of the code below and allow us to trust that TaskStackBuilder is doing what its supposed to.
Simply assuming that code will work is dangerous in practice you could do the following to ease your testing anxieties:
• Implement Shadows for all of those (have fun with that)
• Write a test that checks to manifest to ensure that parent activities are set in the AndroidManifest.xml
• Write integration tests that ensure that the up state is working.
Using these Shadow
To use the Shadow, create your test class and annotate it with the @Config annotation as shown below:
Thats it. You now have a very naive implementation of ShadowTaskStackBuilder to use in your application.
Hussain Ahmed says
Hey Donn, Hopes you are doing great work, I am trying to create unit test code for deep link following you article, but the problem I am getting error with `shadowOf(taskStackBuilder)`, getting error `can not resolve method`, Can you elaborate this.