A self-contained, Dockerized environment for performing chaos testing on native iOS applications using Sauce Labs, saucectl, Sauce Connect, and a programmable mitmproxy instance.
This project serves as a minimal, reproducible example for intercepting and manipulating network traffic from a real device test running in the Sauce Labs cloud.
- Containerized Environment: The entire tunnel and proxy stack is managed by Docker Compose. Start and stop everything with a single command.
- Programmable Chaos: Inject API failures or latency on the fly by setting environment variables.
- Real Device Testing: Designed to work with the Sauce Labs Real Device Cloud.
- Clean & Isolated: The Docker setup is independent of your local machine's network or DNS configuration.
Before you begin, ensure you have the following tools installed on your macOS machine:
- Git: For cloning the repository.
- Xcode Command Line Tools: For building the iOS app.
- Docker Desktop: For running the containerized environment.
saucectl: The Sauce Labs command-line test orchestrator.
NOTE: This project assumes you have proper Sauce Labs credentials configured according to the documentation.
1. Clone the Repository
git clone [email protected]:mmerrell/chaos-canary.git
cd chaos-canary2. Configure Your Credentials
This project uses an .env file to manage your Sauce Labs credentials and tunnel configuration securely.
- Create a copy of the example file:
cp .env.example .env
- Edit the new
.envfile and replace the placeholder values with your actual Sauce Labs username, access key, and desired region.
3. Build the iOS App
The first time you set up the project, you need to compile the ChaosCanary app and its test runner. Run the following command from the project root:
xcodebuild build-for-testing \
-project "ChaosCanary.xcodeproj" \
-scheme "ChaosCanary" \
-sdk iphoneos \
-destination "generic/platform=iOS" \
-derivedDataPath "build" \
-allowProvisioningUpdatesStarting the Services
This command will build the Docker image (the first time) and start the mitmproxy and Sauce Connect containers.
docker-compose up --buildFor subsequent runs, you can omit the --build flag:
docker-compose upWait for the logs to show Sauce Connect is up, you may start your tests.
Stopping the Services
To stop the containers cleanly, press Ctrl+C in the terminal where they are running, and then run:
docker-compose downWith the Docker environment running, open a new terminal window in the project directory and run your tests using run_chaos_test.sh:
./run_chaos_test.sh allYou can run the tests in 4 separate modes:
- normal - runs with a proxy, but with no interference or interception of the requests. You can still view and track HTTP calls within the proxy (via http://localhost:8081).
- failure - runs with a proxy that will return failed status codes for all HTTP traffic emitted by the app. You can inspect these HTTP requests at http://localhost:8083.
- latency - runs with a proxy that inserts an arbitrary 5-second wait before returning the response. It does not alter the response in any way. HTTP traffic for this proxy is at http://localhost:8085.
- random - runs with a proxy which is set to return random failures whenever an HTTP request is made. This HTTP traffic is available for inspection at http://localhost:8087.
- all - runs all tests against all proxies. This is done serially, not in parallel, due to the nature of saucectl's config schema. All above proxies will show HTTP traffic.
✗ ./run_chaos_test.sh
Error: No test type specified.
Usage: ./run_chaos_test.sh {normal|failure|latency|random|all} [--rebuild]You can monitor the intercepted network traffic by opening a browser and navigating to the mitmweb interface at http://localhost:8081.
Note that running saucectl run on its own in this folder will not work, as there is no single config.yml file. The run_chaos_test.sh script was specially formulated to demonstrate the various chaos modes available.
You can control the chaos script running inside the mitmproxy container by setting environment variables in your terminal before you run docker-compose up.
This will make any request containing todos/1 take an extra 5 seconds.
export CHAOS_MODE="LATENCY"
export CHAOS_TARGET_API="todos/1"
export CHAOS_DELAY_SECONDS="5"
docker-compose upThis will force any request containing todos/1 to fail with a 503 Service Unavailable error.
export CHAOS_MODE="API_FAILURE"
export CHAOS_TARGET_API="todos/1"
export CHAOS_HTTP_CODE="503"
docker-compose upYou can configure two types of chaos in "random" mode: timeouts and failures. These are configurable as percentage changes and set as environment variables within the docker-compose files.
With this setting, the proxy will roll the dice on any HTTP Request (or on a specific one if used with CHAOS_TARGET_API) to either the response back normally, or to return a failure rate.
This setting lets you add a random 30-second response time to an HTTP request. `mitmproxy` will roll the dice at runtime for each request.
Setting either field to 0.00 will make the proxy exhibit "normal" behavior for the given feature. These features can be used at the same time to inject both kinds of chaos!
environment:
# Hardcode this proxy to be in RANDOM_FAILURE mode.
- CHAOS_MODE=RANDOM_FAILURE
- CHAOS_TARGET_API=todos/1 # Omit this line to apply chaos to any HTTP Request
# 25% chance of any given request failing.
- CHAOS_FAILURE_RATE=0.25
# Possible error codes to return.
- CHAOS_FAILURE_CODES=404,500,502,503
# Of the requests that fail, 10% will be timeouts instead of error codes.
- CHAOS_TIMEOUT_RATE=0.10
- CHAOS_TIMEOUT_SECONDS=30To run a normal test with no chaos, simply unset the environment variables or set CHAOS_MODE to NONE.
export CHAOS_MODE="NONE"
docker-compose upDockerfile: The recipe for building the container image with Python,mitmproxy, and Sauce Connect.docker-compose.yml: Orchestrates theproxyandtunnelservices.chaos_programmable.py: The Python script thatmitmproxyuses to inject chaos..env: Your secret credentials and tunnel configuration..sauce/config.yml: The configuration forsaucectlto run the tests.ChaosCanary/: The minimal Xcode project for the iOS app under test.