Test on mobile devices during development

I’m currently working on making Nestoria more friendly to users on mobile devices and one of the biggest challenges was easily testing on real mobile devices. This is important, because there is no such thing as mobile WebKit. Apple provides a pretty decent simulator for the iPhone and there are AVDs for nearly every Android device on the market, but there is only so much testing you can do on an emulator. On a real device, things tend to be ever so subtly different and more importantly, you get to see things on a real device in the real scale and in a real context, which happens to matter a lot.
iPhone Simulator

For this reason, I have a desk full of phones. Now I want to access my web app from those devices. I could of course continuously deploy my app somewhere (semi-)publicly accessible, but this makes it painful and time-consuming to make changes. To be truly useful, I need to be able to change something on my development machine and see it on the real device right away.

Try 1: Mac OS X Internet Sharing to the rescue

As I am using a Mac, it’s pretty easy to set up a wifi using the built-in Internet Sharing feature. I then join the network with my phone and enter the IP address of my dev machine and voila: I’m in. If I’d be working on something small and simple, this is where the story would end.

Unfortunately for me, Nestoria is a complex application that has been localised for eight countries. Going to nestoria.co.uk will get you a different Nestoria than if you were to go to nestoria.es. To determine which country is served, it uses Apache VirtualHosts. If I go to http://10.10.10.23/ (my local IP) it has no idea which country to serve.

For regular development, we solve this by having DNS entries for each of the nestoria.TLD domains so that *.localhost.nestoria.TLD points to localhost at 127.0.0.1 (try it). This works great if your development machine is the same as the machine running the application instance, but when testing on mobile devices, that is generally not the case. Navigating to www.localhost.nestoria.TLD causes the phone to try to connect to a webserver running on itself, which surprisingly doesn’t work.

I found two solutions to this problem: one is using a local proxy server, the other one works by running a local DNS server.

Try 2: A local proxy

Since we already have *localhost.nestoria.TLD pointing to 127.0.0.1, the easiest solution is to run a HTTP proxy on that machine and set the mobile devices up to use that. The device will ask the proxy for www.localhost.nestoria.co.uk and the proxy will then go fetch that from localhost, which happens to be your dev machine.

You can pretty much use any proxy you like for this. I personally use Charles as my Swiss army knife, because it allows me to easily set up rewrites and throttling (to simulate slow connections), but you can also use free alternatives like browsermob-proxy or squid. As long as you can install it on your dev machine, it should work.

This worked great for testing on iPhones and other devices that allow you to set the proxy, but I was unable to test some older Android devices using this method. For those, another method was needed.

Try 3: a Local DNS server

If we cannot force the phone to go through a proxy, another solution is to make sure that it gets the IP of my development machine when it looks up www.localhost.nestoria.tld. As it turns out, this pretty easy to set up.

Mac OS X ships with a nameserver (BIND) out of the box. In fact, if you are connecting through internet sharing, you are already using it. By default it is set up to just act as a caching nameserver, which means that for every request, it will ask an upstream nameserver to provide the answer. All we have to do is override some local entries.

Doing this is simply a matter of following this guide. The guide was written for Mac OS X 10.5 (Leopard), but it works fine for 10.7 (Lion).

Adding the entries is easy too. I put the following definitions in /etc/bind.conf:

zone "localhost.nestoria.co.uk" IN {
  type master;
  file "localhost.nestoria.co.uk";
};

and then the following in /var/named/localhost.nestoria.co.uk:

$TTL	86400
$ORIGIN localhost.nestoria.co.uk.
@			1D IN SOA	@ michel.lokku.com (
					42		; serial (d. adams)
					3H		; refresh
					15M		; retry
					1W		; expiry
					1D )		; minimum

			1D IN NS	@
			1D IN A		127.0.0.1

michel IN A 10.10.10.23
* IN A 10.10.10.23

After restarting bind using rndc reload, we can verify that our local nameserver is running and serving the right address:

bash-3.2# dig @localhost www.localhost.nestoria.co.uk

; <<>> DiG 9.7.3-P3 <<>> @localhost www.localhost.nestoria.co.uk
; (3 servers found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46591
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1

;; QUESTION SECTION:
;www.localhost.nestoria.co.uk.	IN	A

;; ANSWER SECTION:
www.localhost.nestoria.co.uk. 86400 IN	A	10.10.10.23

As you can see, the nameserver returns 10.10.10.23 as the address for www.localhost.nestoria.co.uk, which is the IP of my Mac.

If you use the built in Mac OS X Internet Sharing, this is all the configuration needed. All connected devices will automatically use this DNS and get the right address. There is nothing to set up on the phone itself. If you connect through another wifi on the same network, you have to manually set the DNS server to the IP of the Mac, but so far all the phones I have used support that.

Conclusion

Testing on real devices is important. You catch device-specific quirks immediately, feel how the app handles on a real device, on an actual-size screen under representative conditions. Waiting until the app is far enough to deploy on a staging machine is too late. Device testing should be part of the development process from day one. Proxy servers and running a local DNS allow you to test on phones straight from your development machine. Setting it up takes less than an hour. Go do it now!

3 Responses to “Test on mobile devices during development”

  1. Pingback: Matthias Dietrich

  2. Pingback: Michel

  3. Pingback: Inez