Load Test Your Mobile Application API using Rungutan

Written by mariusmitrofan | Published 2020/06/24
Tech Story Tags: serverless | cli | api | rest-api | api-testing | mobile-app-development | mobile | software-testing

TLDR Rungutan is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JWT. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA. The structure, as defined in the Documentation, is simple: You add a "login" key to the test case definition and specify the "path" and "method" for your API login call.via the TL;DR App

Load Testing a Mobile Platform

Load testing a mobile platform is especially hard to do, for the following reasons:
  • You have to simulate traffic from multiple public IPs
  • You have to be able to authenticate as the "machine" device
  • You have to be able to track the requests and create a workflow to support your user interaction
Here comesĀ Rungutan!

What's JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

Adding JWT Auth to Rungutan

{
  "login": {
    "path": "/obfuscated/path/that/does/login",
    "method": "POST",
    "data": "grant_type=OBFUSCATED&username=OBFUSCATED&password=OBFUSCATED",
    "headers": {
      "Content-Type": "application/x-www-form-urlencoded",
      "Authorization": "Basic OBFUSCATED"
    },
    "get_token": {
      "from": "response",
      "key": "access_token",
      "inject_as_header_key": "Authorization",
      "inject_prefix_header_value": "Bearer "
    }
  }
}
The structure, as defined in the Documentation, is simple:
  • you add a "login" key to the test case definition
  • you specify the "path" and "method" (typically POST) for your API login call
  • you add the payload to the "data" field (usually "username" and "password")
  • you include any headers required for this call (usually a "Content-Type" is enough)
  • finally, you specify how to fetch the authorization, from either the "response", the "headers" or simply from "cookies"

Adding some workflow

Well, the hard part was done!
After we sorted out the authentication logic, let's add some workflow paths.
{
  "workflow": [
    {
      "path": "/obfuscated/first/url",
      "method": "GET",
      "data": "",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json",
        "User-Agent": "Pago/1.9.12"
      }
    },
    {
      "path": "/obfuscated/second/url",
      "method": "GET",
      "data": "",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json",
        "User-Agent": "Pago/1.9.12"
      }
    },
    {
      "path": "/obfuscated/third/url",
      "method": "POST",
      "data": "{\"buildVersion\": \"1.9.12\",\"model\": \"Google Android SDK built for x86 Os:29\",\"posBuild\": \"NATIVE\",\"posOS\": \"Android\"}",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json",
        "User-Agent": "Pago/1.9.12"
      }
    }
  ]
}

Putting it all together

Now that you have a workflow and a valid login method, all we have to do is define the rest of the test case:
{
  "team_id": "mobileappdemo",
  "test_name": "open app-> login -> get data -> post something",
  "num_clients": 250,
  "hatch_rate": 30,
  "run_time": 600,
  "threads_per_region": 3,
  "domain_name": "the-mobile-app-api-hostname.com",
  "protocol": "https",
  "test_region": [
    "eu-central-1"
  ],
  "min_wait": 1000,
  "max_wait": 1000,
  "login": {
    "path": "/obfuscated/path/that/does/login",
    "method": "POST",
    "data": "grant_type=OBFUSCATED&username=OBFUSCATED&password=OBFUSCATED",
    "headers": {
      "Content-Type": "application/x-www-form-urlencoded",
      "Authorization": "Basic OBFUSCATED"
    },
    "get_token": {
      "from": "response",
      "key": "access_token",
      "inject_as_header_key": "Authorization",
      "inject_prefix_header_value": "Bearer "
    }
  },
  "workflow": [
    {
      "path": "/obfuscated/first/url",
      "method": "GET",
      "data": "",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json"
      }
    },
    {
      "path": "/obfuscated/second/url",
      "method": "GET",
      "data": "",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json"
      }
    },
    {
      "path": "/obfuscated/third/url",
      "method": "POST",
      "data": "{\"buildVersion\": \"1.9.12\",\"model\": \"Google Android SDK built for x86 Os:29\",\"posBuild\": \"NATIVE\",\"posOS\": \"Android\"}",
      "headers": {
        "Accept-Language": "ro_RO",
        "Content-Type": "application/json"
      }
    }
  ]
}

Running this at every deployment

Now that you have a test, you can include it in your CI/CD process in order to ran it every time you deploy.
The CLI tool can help you integrate it easily with any system.
Here's a sample GitLab CI/CD integration for instance:
image: "python:3.7-alpine"

stages:
  - load_test

variables:
  RUNGUTAN_TEAM_ID: your_team
  RUNGUTAN_API_KEY: your_api_key

before_script:
  - pip install rungutan

load_test:
  stage: load_test
  script:
    - rungutan tests add --test_file test_file.json --wait_to_finish --test_name ${CI_PROJECT_PATH_SLUG}-${CI_PIPELINE_ID}
Or, if you're not a fan of running PIP packages locally, you can use a Docker image. Here's a working example of Docker with Jenkins:
#!groovy

def RUNGUTAN_TEAM_ID=your_team
def RUNGUTAN_API_KEY=your_api_key

pipeline {
  agent any

  stages {
    stage('LoadTest') {

      agent {
        docker {
          image 'rungutancommunity/rungutan-cli:latest'
          args '-u root -e ${RUNGUTAN_TEAM_ID} -e ${RUNGUTAN_API_KEY}'
          reuseNode true
        }
      }

      steps {

        script {
          rungutan tests add --test_file test_file.json --wait_to_finish --test_name ${BUILD_TAG}
        }
      }
    }
  }
}
And if you're using GitHub, you can just reuse the GitHub Marketplace Integration for Rungutan like this:
name: Load test with Rungutan

on:
  release:
    types:
      - created

jobs:
  load:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Load test your platform with Rungutan
      uses: Rungutan/rungutan-actions@1.0.0
      env:
        RUNGUTAN_TEAM_ID: ${{ secrets.RUNGUTAN_TEAM_ID }}
        RUNGUTAN_API_KEY: ${{ secrets.RUNGUTAN_API_KEY }}
        RUNGUTAN_TEST_FILE: test_file.json
        RUNGUTAN_TEST_NAME: ${{ github.repository }}-${{ github.ref }}

Written by mariusmitrofan | CTO at Rungutan
Published by HackerNoon on 2020/06/24