Gatling checks and assertions are how you ensure your load tests are actually verifying the behavior of your application, not just hammering it with requests.

Let’s see Gatling in action, simulating a simple API call and checking for a successful response.

import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._

class BasicApiSimulation extends Simulation {

  val httpProtocol = http
    .baseUrl("http://localhost:8080") // Base URL for your API
    .acceptHeader("application/json")
    .contentTypeHeader("application/json")

  val scn = scenario("API Health Check")
    .exec(
      http("GET /health")
        .get("/health")
        .check(
          status.is(200), // Check if the HTTP status code is 200 (OK)
          jsonPath("$..status").is("UP"), // Check a specific field in the JSON response
          jsonPath("$..version").saveAs("api_version") // Extract a value and save it for later use
        )
    )
    .exec { session =>
      println(s"API Version: ${session("api_version").as[String]}")
      session
    }

  setUp(
    scn.inject(
      rampUsers(10) during (10 seconds), // Gradually ramp up to 10 users over 10 seconds
      constantUsersPerSec(5) during (20 seconds) // Maintain 5 users per second for 20 seconds
    ).protocols(httpProtocol)
  )
}

In this example, the GET /health request has two checks:

  1. status.is(200): This asserts that the HTTP response status code must be exactly 200. If it’s anything else (like 404 Not Found or 500 Internal Server Error), Gatling will mark this request as failed and report it in the results.
  2. jsonPath("$..status").is("UP"): This uses a JSONPath expression to navigate the response body. $ represents the root of the JSON document, .. means to search recursively through all nested objects and arrays, and status is the key we’re looking for. We then assert that the value associated with the status key must be the string "UP".

Additionally, jsonPath("$..version").saveAs("api_version") extracts the value of the version field from the JSON response and stores it in the Gatling session as a variable named api_version. This is useful for passing data between requests or for logging. The subsequent .exec block demonstrates how to access and print this saved value.

The core problem Gatling checks and assertions solve is distinguishing between a load test that generates traffic and a load test that validates application correctness under load. Without them, you might see 99% of requests succeeding at a high throughput, but if the application is returning incorrect data or error codes that you’re ignoring, your test is fundamentally flawed. They allow you to define what "success" looks like from the application’s perspective, not just the network’s.

Internally, Gatling’s check builder is a DSL (Domain Specific Language) that defines a set of rules applied to the response of an HTTP request. Each rule is an assertion. If any assertion fails, the request is considered failed. Gatling provides many types of checks:

  • status.is(statusCode): Checks the HTTP status code.
  • bodyString.is(expectedString): Checks the entire response body as a string.
  • bodyBytes.is(expectedBytes): Checks the entire response body as a byte array.
  • regex(pattern).is(expectedValue): Uses a regular expression to find a match in the response body.
  • css(selector).is(expectedValue): For HTML responses, uses CSS selectors.
  • jsonPath(jsonPathExpression).is(expectedValue): For JSON responses, uses JSONPath.
  • xpath(xpathExpression).is(expectedValue): For XML responses, uses XPath.
  • header(headerName).is(expectedValue): Checks a specific HTTP response header.

These checks can also be used to save data from the response into the session using saveAs("sessionKey"). This is powerful for dynamic requests where a response from one request informs the next (e.g., capturing a session ID).

The jsonPath and regex checks are particularly versatile because they allow you to inspect specific parts of the response body. For example, jsonPath("$..user.id") would extract the id from a nested user object. You can also chain multiple jsonPath checks to assert different parts of the JSON structure.

When you run a Gatling simulation, the report will clearly distinguish between successful requests and failed requests. Failed requests are further categorized by the type of check that failed (e.g., status code mismatch, assertion failure on jsonPath). This granular reporting is crucial for debugging.

One of the most powerful, yet often overlooked, aspects of Gatling checks is their ability to handle conditional logic within a scenario. You can use .checkIf(condition) to apply a check only when a certain condition is met, often based on data previously saved in the session. This is essential for simulating complex user flows where certain actions or validations only occur under specific circumstances. For instance, you might only check for a specific error message if the response status code was a 400 Bad Request, otherwise, you’d expect a 200 OK.

The next logical step after mastering basic assertions is to explore how to chain requests and use data from previous responses to drive subsequent ones, often involving saving and referencing session variables.

Want structured learning?

Take the full Gatling course →