skip to content

In REST Assured, how does given().param() decide whether a value becomes a query or form parameter?

level: juniorimportance: should knowfreq 66%

answer

  1. the verb decides, not you
  2. POST is the odd one out
  3. form body versus query string
  4. queryParam and formParam pin it

basics

~10 s

REST Assured picks by HTTP method: param() values travel in the query string on GET and every other verb, and become url-encoded form fields only on POST. Use queryParam() or formParam() to decide explicitly.

solid answer

~40 s

`RequestSpecification.param(name, value)` stores the pair in a neutral request-parameter map and only decides where it goes when the request is sent. On a `POST` that map is merged with anything added by `formParam(...)` and written as the `application/x-www-form-urlencoded` body; on `GET`, `PUT`, `PATCH`, `DELETE` and the rest it is appended to the query string. That is handy when one helper builds both `GET /v1/routes` and `POST /v1/dispatches` for a snowplough dispatch API, and it is guesswork the moment a `POST /v1/dispatches?dryRun=true` needs a value in each place. `queryParam(...)` and `formParam(...)` pin the destination regardless of verb, so prefer them in shared code. One asymmetry worth remembering: `formParam(...)` on a `GET` is still sent in the query string, and its presence makes REST Assured set `Content-Type: application/x-www-form-urlencoded`.

code

java · 24 lines
java
import static io.restassured.RestAssured.given;

class DispatchParamRoutingTest {

    void listRoutesByZone() {
        given()
            .param("zone", "north")
            .queryParam("priority", "high")
        .when()
            .get("/v1/routes")
        .then()
            .statusCode(200);
    }

    void dispatchPlough() {
        given()
            .param("ploughId", "PLW-114")
            .formParam("routeId", "RT-7")
        .when()
            .post("/v1/dispatches")
        .then()
            .statusCode(202);
    }
}

go deeper

for a junior

Be ready to say that param() picks query or form from the HTTP verb, and that queryParam() and formParam() let you say it outright. Knowing POST is the form case is enough at this level.

for a middle

Explain the mechanics: three separate maps, one decision taken at send time, and POST as the only verb where param() becomes a form field. Mention that formParam() on a GET still goes in the query string.

for a senior

Show the review judgment. Argue for banning param() in shared spec-building helpers because the destination is invisible at the call site, and point out the IllegalStateException a mixed form-plus-body request produces.

for a principal

Own the convention. Decide whether the house style allows verb-inferred parameters at all, and weigh the small convenience against every future reader having to know the verb table before they can review a request.

## What `param()` actually records REST Assured's request builder keeps **three separate parameter maps**: one filled by `RequestSpecification.param(...)`, one by `queryParam(...)` and one by `formParam(...)`. Calling `param("zone", "north")` while building a call against a snowplough dispatch API decides *nothing* about the wire format. It drops the name/value pair into the neutral "request parameters" map and moves on. The decision is deferred until the verb is known — that is, until you call `get(...)`, `post(...)` or another verb method on the specification. That deferral is the whole point of `param()`. It lets one helper method build a request that a `GET /v1/routes` and a `POST /v1/dispatches` can both send, and it is also the reason two engineers can read the same test and disagree about what the server will receive. ## The rule applied at send time | verb | `param(...)` lands in | `queryParam(...)` lands in | `formParam(...)` lands in | |---|---|---|---| | `GET` | query string | query string | query string | | `POST` | url-encoded body | query string | url-encoded body | | `PUT`, `PATCH`, `DELETE`, `HEAD`, `OPTIONS` | query string | query string | url-encoded body | Read the first column carefully: **`POST` is the only verb on which `param(...)` becomes a form field.** The usual shorthand — "GET means query, POST means form" — is right about both ends and silent about the middle, and the middle is where a `PUT /v1/routes/RT-7` quietly puts your value in the URL. The second surprise is the bottom-left cell of the `formParam` column. On a `GET`, form parameters are not dropped and they are not sent as a body: they are appended to the query string like everything else, and their mere presence makes REST Assured choose `application/x-www-form-urlencoded` as the request content type. The call works; it just documents the wrong intent. ## Why the explicit forms exist - `queryParam(name, value)` pins a value to the query string on **every** verb. - `formParam(name, value)` pins a value to the url-encoded body on every verb that carries one. - `param(name, value)` delegates the choice to the verb, which is convenient in shared setup code and ambiguous in a test someone else has to debug. - A `POST /v1/dispatches?dryRun=true` that also posts `ploughId` and `routeId` as form fields **cannot** be expressed with `param(...)` alone — you need `queryParam("dryRun", true)` beside `formParam("ploughId", "PLW-114")`. - The three maps are independent, so mixing the explicit calls with `param(...)` in one request is legal and the values simply go to their respective destinations. ## A dispatch example, both ways ```java // zone and priority both end up in the query string given().param("zone", "north").queryParam("priority", "high") .when().get("/v1/routes") .then().statusCode(200); // ploughId and routeId both end up in the url-encoded body given().param("ploughId", "PLW-114").formParam("routeId", "RT-7") .when().post("/v1/dispatches") .then().statusCode(202); ``` Both calls read almost identically and send parameters to two different places. That is exactly the readability cost interviewers are probing for when they ask this question. ## Edges that bite 1. **Form parameters and a body are mutually exclusive.** On a verb that may carry a body, setting both form parameters and body content makes REST Assured throw an `IllegalStateException` saying you can send one or the other, not both. 2. **A value-less parameter is legal.** `param("dryRun")` records the name with no value, and it is rendered without an `=` sign rather than as an empty string. 3. **Multi-value is built in.** `param("zone", "north", "south")` and the `Collection` overload both record two values under one name; there is a separate `Collection` overload on `queryParam(...)` and `formParam(...)` too. 4. **Repeat calls merge rather than replace** by default for all three maps — a second `param("zone", ...)` adds a value instead of overwriting one, which is governed by `ParamConfig`. 5. **A query string typed into the path still becomes query parameters.** Writing `get("/v1/routes?zone=north")` is parsed and folded into the query-parameter map rather than passed through verbatim. ## What to say in an interview Say that `param(...)` is verb-driven and therefore under-specified, that `POST` is the single verb where it becomes a form field, and that you use `queryParam(...)` and `formParam(...)` in anything a colleague will maintain. Then add the detail that separates a user from someone who has read the behaviour: `formParam(...)` on a `GET` is still sent in the query string, and it changes the request content type on the way past.

  • What happens if you set both formParam(...) and body(...) on the same POST in REST Assured?
    REST Assured refuses to send it. When a verb may carry a body and both form parameters and body content are present, it throws an `IllegalStateException` saying you can send form parameters or body content, not both. Choose one: build url-encoded fields with `formParam(...)`, or send the whole payload with `body(...)`.
  • Does formParam(...) do anything on a GET request in REST Assured?
    Yes. On a `GET` the form parameters are appended to the query string alongside the query parameters, and their presence makes REST Assured pick `application/x-www-form-urlencoded` as the request content type. It works, but it misleads the next reader — on a `GET`, write `queryParam(...)`.

saying these in an interview costs you the question

  • Claims param() is always a query parameter regardless of verb
  • Thinks param() inspects the Content-Type header to decide
  • Says formParam() is silently ignored on a GET request
  • Believes queryParam() moves into the body on a POST
  • Mixes form parameters with body() and expects both to be sent