There’s a handy thing in Espresso called the ServiceTestRule, which is for testing Services. Yay. I thought it was just what I needed until I read this bit of the documentation…
OK, first up – refactoring my IntentService. This class already did very little, poking something else depending on the kind of Intent it received, and then triggering another intent when the work was done. So it was pretty straightforward to refactor everything out into a Helper class. This class takes the intent, does the work, and returns an intent, which is then thrown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This makes testing more straightforward because I just need to mock the helper, and return the requisite intent for whatever was supposed to happen.
Testing the helper was also easier, which made it easier to be more thorough. The only thing I ran into was needing an ActivityRule (in order to get the ContentResolver).
But – what about testing the thing that calls the IntentService? That was harder. Firstly, I set up IdlingResources, as explained by Chiu-Ki. But there’s an intent launched and received as the activity starts, which complicates some things. I ended up verifying and resetting all my mocks for each test twice – once after setup completes, and the second time after the end of the test.
First I got all tests running individually, but when I started combining them I was running into issues that I was pretty confident were concurrency issues. I asked Chiu-Ki to check, and on a faster emulator (and probably a better computer) things were fine… but they weren’t for me. I wanted to add UiController.loopMainThreadForAtLeast as explained in this blogpost, but that looked a bit complicated.
To check if I was right, I added Thread.sleep(100); after each mock of the Helper. And yay! Everything worked! My enquiries into UiController.loopMainThreadForAtLeast hadn’t left me optimistic about it, so I left it at that.
Ideally you don’t want to be using sleep() in tests. But I subscribe to the idea that any test is better than no test at all. Maybe I’ll be able to rewrite things so that this isn’t necessary, but I’m not that hopeful – part of the reason why this is necessary is because it’s a complicated process, and I’m trying to improve the perceived performance by breaking things up, which is why the Activity is so entwined with the IntentService.
Apparently one option is Fork, which looks super useful. However if you use it, you need to run your tests from the command line. I really want to be able to run my tests from Android Studio (especially as I haven’t set up CI on this project), so I guess I will live with sleep()
The picture above shows what am I testing: the home screen of my app. There is a camera button, a gallery button and an inspire button. All of these launch intents, but the camera and gallery buttons launch intents that are expected to return something – an image – either from the camera or the gallery.
First up: how the hell do you test an intent? I started with my straightforward Dagger/Mockito setup, but my tests were failing because they were launching things – the camera, or the gallery – which I then couldn’t get out of to continue my tests.
The answer is the IntentsTestRule, which extends ActivityTestRule (if you’ve written a test for an activity, you’ve probably seen this). This took me a little while to make sense of, mainly because I kept getting this error saying that something had been initialised twice. I was launching the intent in my test, and also calling Intents.init(). Turns out, you don’t need to do that. Having an intent rule is a little bit of magic. It just launches itself.
I have a very straightforward test that just checks that things have loaded, and it’s one I kept coming back to in order to see how things work. Here it is.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
I think sometimes it seems like it’s not worth having tests like this because all of this will be covered elsewhere. Not true. I always include really straightforward tests. If they’re useless, then who cares, you’ll never think about them again. But in practise I return to them again and again when I’m debugging.
First up I got my gallery test working. I had no idea what I was doing, so basically everything was broken, so I started by being really general and then getting more specific once I had it all working together. For example, figuring out which package it was in was a PITA, so to get thing working I used this handy catchall:
intending(not(isInternal())).respondWith(result);
This will return my “result” intent to every intent outside the app. Definitely not as exact as we want in our tests, but really useful for getting things working. In theory you can check ActionType, which in the case of the gallery is Intent.ACTION_PICK. In practise, this Intent was not being captured.
I eventually managed to find out what the package was by using:
Intents.assertNoUnverifiedIntents();
This is a check for unverified intents, and handily in the error message it gave me it returned the package that the intent was coming from. So, for the gallery I had:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Here I had some SUPER FUN debugging. By super fun I mean: like stabbing myself in the eye with a blunt implement. Luckily there were no blunt implements to hand and I have a strong sense of self preservation. Once I got my intent working, it seemed like the test was done. I added some validation of a new activity getting launched. Also, I decided to add validation on something else should happen – that the image processor gets reset. This should be straightforward, right? Everything is set up with Dagger and injected, I have my Mock Providers, and Mock Component for my tests. I just need to verify.
Nope. It didn’t work, and it took me a long time to figure out why.
The issue was that I didn’t have my mock objects in my activity. I thought this was because the activity was being relaunched, but that was a red-herring. The mock objects were being interacted with in the new activity, so I knew that my setup was somewhat right, but clearly not completely! Obsessively trying to hunt down the cause for this did help me fix a bug though – I had a fall through in a switch statement in my activity.
Breakpoints had failed me (I was putting breakpoints in my tests, but the debugger wasn’t stopping on them – grr) I returned to my very straightforward test, and started just adding some log statements. This helped me figure out the problem: onCreate() in the HomeActivity was being called before setup (labelled with the annotation @Before) in my HomeActivityTest. So I had real objects in my activity under test, because it was using the usual dagger components, not the test ones.
Once I knew what the problem was, I knew what to search for to find a fix for it. The conclusion: I needed to subclass the IntentsTestRule, and override beforeActivityLaunched() to put my pre-activity setup code in it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
There was no new code here – this was in my standard test setup. It’s just in a new place. I made it within the test, so it’s a private static class. As I add more tests I may move it out to be reusable. For now, it’s only binding the components I need in this test. If I move it out, I’ll need to bind everything.
Then I added the test for the result failed test. This is more straightforward so it’s tempting to add this first, but in practise I never add my tests for nothing happening first because I never have any confidence that they actually work until I can break them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Notice that in the first test, I assert my image processor gets reset. In this one I assert that it doesn’t get reset.
Next, testing the camera intent. It’s basically the same, but I also have to mock the Uri, because when you take a photo on Android you first have to allocate space for it. I have complained about this before so I will refrain here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
And again, we check that it works when the result is cancelled.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
It was tempting to delete the mock fileUri here, because it seems like we don’t need it, but actually if I did that it would not be testing what I want it to, even though the behaviour (nothing happens) would be the same.
In both the cancel intents, I wanted to assert that no new intent is launched, but it’s wasn’t clear how to do that. Intents.assertNoUnverifiedIntents() was failing on things I’d stubbed. I realised that I had to stub and expect things, so I needed an intended() for each intending(), and voila! It works. Once I discovered this, I went back through all of my tests and added the requisite lines.
Finally! My most straightforward intent test, and you may wonder why I didn’t start with it. As do I. There’s a third button, inspire, which launches the web browser with a specific URL. Note that this test uses intended() rather than intending(), and that it comes after the intent is launched – not before. At the end, we assert no unverified intents.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Note: one tutorial I found suggested stubbing all external intents with the blanket catchall, and setting it up in a method annotated with @Before. I didn’t do this and I don’t think it’s a good idea. Launching an external intent is something that you should be capturing deliberately, and that’s the whole point of testing it. I would be more likely to go the other way and assert no unverified intents in a method annotated @After!
Chiu-Ki took me through getting Dagger and Espresso set up in the first place, ages ago. And if you’re not lucky enough to be friends with her (and passing through Denver), she has a bunch of resources for testing on Android on her website.
On iOS I do this using a button (with different pressed state), and I figured it would be the same on Android, but turns out, no.
Step 1: Add a second image to the XML, and set the visibility to “gone”.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
I suspect one of my limitations as a programmer is that I don’t hack. I don’t beat away at something until it works. I read things, and I reason about it, and I write a lot of tests.
This makes me very effective on platforms I’m familiar with, but I worry I’m as a result not as effective when I’m picking new things up as someone who will just hack away. I’m searching for the moment when things start to make sense.
I’ve done some Android over the past few years. I really wanted to learn it, but when I started working on it, it really wasn’t that fun. It was an over-engineered codebase, and as I tried to find my way in it, the feedback I got in code review was often of the “I would have done it differently” variety. Often that way didn’t even work, so that was… rewarding.
The first breakthrough was that a lot of stuff is just more work than iOS. For instance, if you want to take a photo on iOS you just like… launch the camera and implement the delegate.
If you want to take a photo on Android, you mount the hard drive, allocate space for the photo, launch the intent, and handle it returning. I always thought managing hardware and memory was a job for the Operating System, but what do I know about Operating System design, anyway.
Aside: as I learned this lesson one of the guys I worked with told me that I must be wrong about how annoying it is to take a photo on Android. Then – once I had got it working – sent me a code review of his from a previous project and said (I paraphrase) “that is the right way to do it actually, because that’s how I did it too”.
So I returned to Android this year with a degree of trepidation. I really wanted to be better at it, but based on what I’d learned so far about it, mainly I was happy in Java and I’d learned maybe how not to do some things, but as I’ve commented before, that doesn’t always teach you that much.
Last week was the ~2nd week this year where I was able to focus on Android and things finally started to click. It was so exciting, because now I feel like I can pick up small bugs here and there, whereas before I felt I needed minimum 2 days to make progress. It’s like going from navigating with a compass to having a compass AND a (slightly fuzzy) map.
The big thing that clicked was understanding the ways in which the platform encourages bad design.
On iOS, that thing is mixing View Code and Control Code. The more tools I add to my arsenal to handle that, the better architected my iOS apps became. There’s another area of mixing model and persistence code. Really on iOS the design problem is mixing things that would be better separated. Learn that, make an effort to keep things apart, and everything seems more possible.
On Android things are very separated. This is not a problem you run into. The view is defined in xml. Any background processing work needs to live in a “service”. In fact on Android separation of things goes so far the other way, that the problem is state. When you rotate your phone, the activity gets recreated. So if you have anything with state, you need to save that state. If you have anything that might be happening in the background, you need to handle getting the same service.
This means:
I don’t even know how you would get a stateful Android app working without Dependency Injection (luckily I had Chiu-Ki to help me with this, because it’s tricky).
This encourages the use of Singletons (ai!) because it’s an easy way to make sure you get the same service when the phone is rotated.
Automated dependency injection is nice and good for testing, but it can allow you to have very complex object relationships. I don’t see it as some panacea for good design, more as a something that obfuscates bizarre things you have done.
This is an app I’ve been porting over from iOS and it’s fascinating to me what’s different. Some things were easier, and some things were harder. But, Android makes a lot more sense, and I got things working enough to send out a beta, so that is exciting.
A while ago, I wrote this blog post on creating and comparing UIImages. That code allowed me to develop the image processing part of the app against my unit tests, which was really, really helpful given that I rewrote it about four times to make it performant enough.
So, when I started writing Android code it was one of the first things I ported. Firstly let me say – way easier on Android than iOS. A tiny difference in the API was a gotcha, iOS takes arguments x, y, width, height, and a comparable function on Android takes x1, y1, x2, y2. But other than that it was much more straightforward, basically because of how much easier it is to delve into the pixels.
To create a one color image, you can just set the color and draw to the canvas:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
But if you want to set individual pixels you can just call setPixel(). So to create a 3×3 2-color-alternating image (I find this a really useful test image):
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This is similar to the way we can create an image from an array of colors:
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
On iOS creating from an array was sufficiently complicated that I felt like the simpler creation methods were also worthwhile. On Android I’m less certain! I may refactor them to just call the array function.
But now we have made our test images, we need to be able to compare them. As before, I’m defining two images as the same iff (if and only if) they have the same width, height, and the pixels are the same color. For now I’m able to do an exact comparison, but I may add some kind of tolerance here as the code evolves.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
These helper methods have been a really important part of my testing strategy on both platforms – the image processing is the core of the app, and I want to be sure it works really well.
I’ve started to port Show and Hide to Android. There’s still a lot to do, but I hit a milestone of having it working end to end on the emulator last week, which was exciting.
One of my friends asked if I was using any libraries to make it easier, and the short answer is no. But I think the long answer is potentially interesting so here it is.
1. I want to learn Android
I don’t think there is a better way to do that than building an app from start to finish on that platform. The more libraries and bits and pieces you work with, the more you learn the intricacies of that system, rather than the platform itself.
Sometimes that is exactly what you should be doing. There’s often no point doing something that has already been done. But cross-compiling isn’t re-implementing something that exists already, it’s choosing to write something a third way and hope it works well enough on both platforms (more on this later). At this point, if I haven’t written an entire app already, I don’t think I can really have the information to just decide that cross compiling is the way to go. How could I compare the experience to a problem that I haven’t solved?
2. UI Code
Fundamentally there are two reasons why I don’t think we will ever have a good cross-platform UI solution. The first is that with major releases every year, it would be a huge amount of work to maintain such a thing. Almost no-one has the resources to run such a project, and of those that do even fewer have the incentive. The second is that the UI patterns on iOS and Android are different enough that what is “right” on one platform won’t feel “right” on the other.
3. Non-UI Code
Here I think there can be a good argument for a cross platform solution, depending on what you’re doing. Libraries like Parse are interesting, making it easier to abstract persistence and networking out and share it.
But – the core of Show and Hide on iOS is about 400LOC of optimised C code that is tied to the way that the platform represents images. I don’t even know if that would be possible to share x-platform, and the chances of it being performant enough for my purposes is vanishingly small.
Because it’s a relatively small amount of code and I deeply understand it, moving it to Android took only 1-2 days. I’ve yet to see whether it is performant enough, but this way I’m also in a much better position to optimise it.
Porting the Architecture
Instead of using cross-platform compilation what I’ve been doing is:
Building the UI according to Android best practises (or trying to).
Keeping (initially) the same function definitions for non-UI code.
Changing the implementation to make sense on the platform – e.g. the way that iOS and Android represent images are completely different.
This means that:
The two apps have very similar architecture – the same objects, with similar methods on them.
The unit tests are near identical: given this input expect this output.
It’ll be some time before I can declare success on this as a strategy, but I’m cautiously optimistic.
My app idea – a place to collect things that inspire, that can be found searching their tags, location, or type. I understand that people can and do use any number of other apps for this – Twitter favourites, for example, this is just supposed to be a purely happy and motivating place on our most intimate device – the phone.
This has probably done elsewhere (I haven’t looked) but fulfils certain requirements that make it (I think) a good first project.
Why This Project
Tapping into share intent. I like the idea of an app being an expansion pack for other apps – something that is possible on Android, but not so much on iOS. I want to build the kind of app that I could only build on Android.
Visually minimal, content is king. I care a lot about how things look, but have pretty minimal UX skills myself. Therefore, less UX the better.
Components to Figure Out
Storage. Inspirations need to be stored (offline experience – do not plan on starting with a server) and searchable by tags (although ideally free text search on associated notes, too).
Subscribe. This is the initial way in for data, subscribe to the share intent and find ways to extract different kind of content – images, text (snippets, web pages, emails), people, places. Are there other intents that I want to subscribe to? Also need to be able to create something without an intent – just a note to self, or comment in conversation.
Sync. I’m not initially aiming to have a server and sync across devices (nice add on, though, if this project keeps interesting me), but I don’t want all data to be lost when the user gets a new phone. I think I can use the standard backup for this, but I am not sure – need to investigate.
Display. Obvious – if the user can’t see it, whatever data is there doesn’t matter. I want users to be able to look for tags, or just a random “I feel low, inspire me!”.
Aside – I love the concept of this event. Great opportunity to hang out with your laptop, the only goal being to suck less at something by the end of the day. I would love to do this every month. Lots of women about, which was great! And I loved the Stripe offices – they have real Dr Pepper!
I think I was in the spirit rather than the actual definition of the event because I’m familiar with Java (I even have readability in the kind written at work). And actually I’ve been doing some Android programming, but what I haven’t done – but want to – is write an Android app from scratch. I plan to do a bunch of posts on my experiences, from application concept to… however far I get.
I started by downloading the SDK bundle – this comes with Eclipse and is all set up and ready to go. Really easy!
Then I went through the Building Your First app tutorial, I made the default list-detail view and got it running on the emulator and my phone (much faster than the emulator).
Then I gave Android Studio a go, as one of my friends said it was a lot faster.
I just imported the existing project from Eclipse (even though it didn’t really do anything). Again, I got it running on the emulator (still slow) and the device (not noticeably faster than from Eclipse).
I’m not completely sure which one to use, I’m used to Eclipse but Android Studio seems pretty easy. I think it should be be fine to switch between them though, so I will give Android Studio a go for a while.
Some Gotcha’s
Don’t forget to start the Android emulator before trying to run your application.
Getting to Developer mode on a physical Android requires going to settings, about phone, and then tapping “build number” until it says “you are now a developer” – 7 times. You’ll then probably want to turn on USB debugging in the Developer Options menu that appears in settings.
I set a high minimum version to make things easier (didn’t matter in this, but will later when I’m making my actual app) – make sure your device is meeting this! I hadn’t used that Android in a while, so the OS was out of date.
Cookie Consent
We use cookies to improve your experience on our site. By using our site, you consent to cookies.