Mobile app testing: ManInTheMiddle can fool you!

In one of my previous posts I’ve already shared with you how to get insights about tested mobile app with usage of proxy tools (please see again: Mobile app testing: underneath #1). Even without proxy tool in use by you, chances are quite high that between your app and the backend service your app communicate with, sit couple of different MITMs (man-in-the-middle), like api gateways, api proxies, etc. Each of it, as well as your proxy tool, can modify the request on the fly and you may even not know it.

Here is a simple example of header that your request can have included:

Accept-Encoding: gzip

In short it says:

Hey, I’m able to understand following encodings (e.g. compression algorithm like gzip, br, deflate, etc), so if you can, please compress it for me, to speed sending it over the network and save some network quota

szekar1

other possible values it can take (but not limited to) are:

Accept-Encoding: compress
Accept-Encoding: deflate
Accept-Encoding: br
Accept-Encoding: identity
Accept-Encoding: *

// Multiple algorithms
Accept-Encoding: gzip, compress, br

// and weighted with the quality value syntax:
Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5
Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1

You may ask ‘why am I writing this?” this is well defined in http standard and this is nothing new.

Right, but …

(yeah, there always seems to be at least one “but”)

… there is also a chance that not all parties around you have implemented it properly or from ‘cover to cover’ or simply are doing some “defaulting” to their preferred values.

You don’t believe me?

Here is an example of very popular iOS elegant networking framework: Alamofire. By default, mentioned earlier, Accept-Encoding header is set to (based on the version of iOS):

if #available(iOS 11.0, *) {
    encodings = ["br", "gzip", "deflate"]
} else {
    encodings = ["gzip", "deflate"]
}

so far so good, nothing unexpected. But then there is one additional “cherry on top”:

return .acceptEncoding(encodings.qualityEncoded())

which assign (the qualityEncoded() function) different values to *q* values starting from 1.0, so at the end the header you are sending is something like:

Accept-Encoding: br;q=1.0, gzip;q=0.8, deflate;q=0.8

And here it starts tricky, as this requests goes through so many layers that one of them may have a bug? missed requirement? a feature? call it by your own words … that changes this header to something different or worst, ignoring it at all and instead of having back compressed data back you get it back in plain JSON, not compressed at all …

╭─szekar1@home ~/
╰─$ ll
-rw-r--r--  3.1M Jul 15 22:22 response_api.json
-rw-r--r--  509K Jul 15 22:22 response_api_gzip.json

do you see the difference between file sizes? and imagine your phone is getting uncompressed, 6 x times bigger, data each time … and now imagine you are abroad on low roaming data plan … ouch

and now you say, that of course you can check that via proxy tool, that expected compressed file is returned to your app, and you’re right, with one small catch – you need to know your proxy tool very well as there may be some tricky settings as this one:

If it is checked (and by default it is), then all you will see is the new shinny “Accept-Encoding: gzip” header, that is highly recognizable and should work in 99% cases, but is it your case as well? if your header contains *q* values or some old compression formats like ‘compress’ then I would not be so optimistic.

What I would like you take from this post is that one proxy tool sometimes is not enough if you are not aware of its ‘magic’ behind your backs (in the proxy tool you are seeing the same header as in the server logs, the size of the response in the tool seems to be compressed, all the dashboards are telling you that your backend service generated compressed data, etc, but still the client, your app, is consuming too much precious roaming data quota).

Pick tools to your toolbelt carefully and invest your time in finding what all these small checkboxes are for, compare it with another one and verify the results are the same and at the end study your app, frameworks and libraries you are adding to it to know what to expect from simple thing like getting compressed data back from your backend service.

Happy testing, happy learning!

Mobile app testing: underneath #2

I’ve started this year with mobile app testing blog post already but as far as yesterday my #bugmagnet manifested itself once again and this is follow up on the tweet:

Screenshot 2019-03-02 at 21.04.32

As you can see on the screen there are 3 instances of the same app – something that is rather unusual. The reproduction is quite easy and fast:

  1. Start the app
  2. Move away from the app either by pressing “Home” or “Back” (yes, I’m on Android) buttons
  3. Check it is in the recently used apps by pressing an “Overview” button
  4. Start the app once again
  5. Repeat steps #2 and #3

as a bonus, there is another case (not so easy to test when you are not in the development team) when a notification is received – when you tap on it, it should open the app (or bring it to the foreground).

Expected behavior: in either case, there is only one app instance (process) running.

This is closely connected to the way Android handles Activity lifecycle – there are really good writings available already with explanation what is happening when and it would be silly from my side to attempt to rewrite it and try to describe it to you better than done by official Android documentation or Google Android developers – just please go and read these resources – especially the Jose Alcérreca “cheat sheet” series on medium.com.

activity_lifecycle

So why I’m describing this to you?

Because like you should use proxy tools to develop and test your mobile app (see my #1 post on the “mobile app testing: underneath” series – btw. who would imagine I will call it “a series”), you should as well be aware what’s going on underneath your app on the platform when you interact with it with buttons, moving app between foreground and background, interrupting it with a text message, calls, notifications (either for it or other apps) or by screen rotation, etc.

I hope you will find your app even more interesting now – knowing all the onCreate(), onResume() and other possible Activities and cases where they are called – happy testing!

Mobile app testing: underneath #1

In the yet another 2018 review post, I’ve given you a short sneak peek about “new challenges” I’m going to take in 2019 – a new project for me, a new approach to testing, or should I say to SW development? let’s leave this rant for another time …

What I would like to share with you here is a ‘must have’ thing (IMHO) for mobile app testing (a big part of my new challenge) – knowing what’s going on behind the scene, underneath nice, modern, UX-tuned frontend.

Shall we talk about HW, devices, OSes, platforms’ APIs, networks – for sure!
Shall we talk about app architecture – for sure!
Shall we talk about native, hybrid, web apps – for sure!

But let me start with something else – exploration and learning what the app is doing when you interact with it, especially when you try (like me after joining new project) to figure out not only all the features the app has, but more importantly what is required for it to run, perform designed actions, survive ‘offline’ mode, etc.
If you are lucky you will have up-to-date documentation next to you to follow …

yeah … right -> RTFM & “working software over comprehensive documentation

but back to business … PROXY #FTW

There is a quite nice number of proxies available on the market that can help you to capture traffic your app is generating to the backend (or any other services), to name
a few:

Please consider your use cases, platforms on which your app and laptop (desktop?) are running, licensing, UX, learning curve, availability and clarity of documentation, etc before you jump in for one or another solution – the final call is yours 😉

I will skip the “how-to-part” about checking your PC/laptop IP address, how to configure the proxy on your mobile phone, etc – each tool mentioned above will guide you through this process in respective documentation e.g. here is the Fiddler one or you will figure it out using your favourite search engine.

Oohh … maybe just small hints:

  • after you completed setup please try if things are working on basic http requests before you jump directly to your app, chances are that it uses https already so by default you will not see this traffic in the proxy
    • if you are using Fiddler then there will be this test page to try out:
      http://ipv4.fiddler:8888/ where you may want to replace 8888 with the port you configured
    • Troy Hunt also can help you with this huge collection of pages that are still not using https and are quite popular in your country – just visit one of them in the mobile browser or check if the same is available as a mobile app – I will bet more than 90% of those apps are also not using https yet
  • if you see http traffic but are missing https one – just install the certificate for the proxy tool (Fiddler example) that should help
  • one another case you may struggle is when no traffic is visible, neither for http nor for https … if you are running behind a firewall (and who is not these days?) you may want to check if incoming traffic is not blocked by it for your prefered tool …

    firewall_fiddler* to solve this you may need administrator/root privileges – no problem on personal hw but maybe a little more tricky on your business laptop 

Having working traffic capturing in place, start your app and check what calls are made when switching between screens, clicking buttons etc.

Hopefully, you will end up with a better version than me:

basic_req_resp_calls

Use it to discuss the flow with your team:

  • are the calls the right ones?
  • have you spotted strange things?
    • repeated calls – shall there be any?
    • any 5xx / 4xx?
    • what about timings? are the calls “fast” or do you consider them as “slow”?
    • how much data is sent over the wire? are the app/backend using compression?
    • go through API testing heuristics/mnemonics like e.g. LHTRAFFIC, POISED, VADER
  • follow “The Boy Scouts Rule” and document your findings for the team to “Always leave the campground cleaner than you found it”

When you are done with basic capturing of the requests and responses, explore other functionalities of the proxy tool, try to modify responses on the fly, delay them, drop them, be creative and see how your app is dealing with it, because strange things happen and if not you then someone else will try them when your app hits App Store or Google Play.

I hope you will find your app even more interesting now – happy testing!