Charlie and Me: Testing Native Mobile Apps With Charles Proxy

“Charles is a tool that you often use when you’re having a bad day.”
–Karl von Randow, Charles author

We’re going to have to disagree with you there, Karl. At HotelTonight, we use Charles Proxy all the time to stress test and debug our mobile application — even on good days.

Charles helps us solve problems large and small; and here are four tasks we use it for:

  • Simulating poor network conditions
  • Client-side vs. server-side debugging
  • Bypassing server cache
  • Modifying data in API responses

1. Simulating poor network conditions

An important part of the testing and debugging process is seeing how the mobile app performs under different (read: weak) network conditions. For example, we can test whether a failure notification properly displays by simulating a server timeout with Charles’ built-in network throttling and breakpoint functions.

Let’s see what it looks like when we test the promo code function with a simulated slow connection.

First, we simulate a poor connection with the Throttle Simulator:

Then we run the app. You can see below that over a slow connection, our app successfully displays the on-screen loading spinner while waiting for the server to respond, just as planned:

We also run interruption simulations with Breakpoint Tools. Let’s see how it works when we simulate a timeout by setting a breakpoint on the response:

We see that it successfully triggers a failure notification. Works!

Finally, using a breakpoint, let’s test a complete network failure that occurs during an API request. But this time, we’ll abort the request after it’s made:

Once again, the app successfully lets the user know a network error occurred.

2. Determining whether issues are on the client or server side

One of the first things to do when debugging an error is determine whether the problem is being caused by the app or coming from the server side. Charles can help us figure this out by letting us examine the code while we run the application.

Here, the app is being used in English to view hotels in Montreal, and it’s delivering a mix of local (French) and native (English) copy. Naturally, this is a big problem. And the bug that’s causing it is a problem too.

Here we can use Charles to look into the request headers while our server determines which copy to display. Let’s take a look at the Request tab to see the Accept-Language value the app is sending:


Which is fitting, as sometimes devices can be stubborn and continue to set the wrong language-locale request header values even after the device’s language and local settings have been updated.

Charles helps us easily diagnose these types of problems, saving us tons of time.

3. Get around the server cache

Like many mobile apps, we use a client-server architecture, which allows us to utilize data caching on API requests to improve response times for clients. Often during testing, we’ll want to see how the changes we’ve made on the server display on the app, but we’ll get the cached input instead of the updated changes.

For example, let’s look at our cache of API responses for our hotel inventory:

If we make the same request within a certain time frame, we get a 304 (Not Modified) error, which tells us that the server is trying to return the cached information rather than the updated information.

Charles allows us to easily turn off caching for a specific API endpoint by simply right-clicking on the endpoint and selecting “No Caching” from the contextual menu:

Easy peasy. No more 304s.

4. Modifying data in API responses

We have multiple test environments in addition to the production environment. Sometimes when content is created in the test environment, the test server response includes a URL for the production server in the API response for that content (we know, we know — not ideal).

Here’s an example of that for a launch modal image that points to the production environment, instead of the test environment, to get an image:

And here’s the server response:

There are a few ways to fix this problem:

  • We can use the Map Remote Tool to point individual requests to a different server.
  • We can use the Rewrite Tool and rewrite the image request to point to a different location.
  • We can set a breakpoint on the response to manually edit the image’s location.

The beauty is that we can choose which fix to apply based on other configurations on the app. It won’t always make sense for us to completely change the server route, so we can use the breakpoint to manually edit the location, which comes in handy if it’s an individual problem.

Here’s what setting and editing a breakpoint looks like:


Long story short: Charles is awesome, and we use it almost every time we test our app. The ability to monitor the interaction between the server, the client (our app), and even third-party API calls is integral to understanding how our app works and its overall performance. It’s an excellent debugging tool and has become an essential one that we couldn’t do without.

Before we go, here are a few tips on working with Charles that our team put together:

Switching Proxy Settings
Changing the manual proxy settings on each device to enable multiple instances of Charles on multiple test devices for multiple team members…is a multiple of tedious and painful. Similarly, finding out that you forgot to disable Charles on a test device when you start to see a bunch of new requests added to your recording session by someone else can be troublesome.

On Android Lollipop (5.x and earlier), we recommend using the Proxy Settings app to quickly toggle proxy settings for Charles. On iOS and Android M and higher, we have yet to find a good solution. While we did have limited success in creating an iOS configuration profile with the manual Charles proxy settings already set and then installing it on the device, doing so seems to work for only a little while and to stop working over time. If you have any iOS or Android suggestions, let us know here!

About Charles and Getting Started
There are many sources online explaining what Charles is and how to configure it for mobile devices. To use Charles on a mobile device, follow these instructions, install a cert on the mobile device, and (as needed) enable SSL proxying for desired host names.

Recommended Charles Reading
Beyond DevTools: Debugging by Proxy
Debugging HTTP on an Android phone or tablet with Charles proxy for fun and profit
Tutorial: Using Charles Proxy With Your iOS Development And HTTP Debugging