Scripting and Testing

Test Script Examples

Test Script Examples

Copy-paste patterns for the most common API testing scenarios. Each example is self-contained and ready to drop into the Test Script editor of any request.


Checking Status Codes

Expect exactly 200

fc.test("Returns 200 OK", function () {
    fc.response.to.have.status(200);
});

Expect one of several success codes

Useful for POST endpoints that return 200 or 201 depending on whether the resource was created or already existed:

fc.test("Request succeeded", function () {
    fc.expect(fc.response.code).to.be.oneOf([200, 201]);
});

Expect any 2xx status

fc.test("Response is a success", function () {
    fc.response.to.be.ok;
});

Expect a specific error

When deliberately testing that an invalid request is rejected:

fc.test("Missing fields returns 400", function () {
    fc.response.to.have.status(400);
});

Checking the Response Body

Check that a field exists

fc.test("Response has an id", function () {
    var body = fc.response.json();
    fc.expect(body.id).to.exist;
});

Check a field value

fc.test("User is active", function () {
    var body = fc.response.json();
    fc.expect(body.status).to.equal("active");
});

Check a nested field

fc.test("Billing address has a country", function () {
    var body = fc.response.json();
    fc.expect(body.billing.address.country).to.exist;
});

Check a field is a number above zero

fc.test("Price is positive", function () {
    var body = fc.response.json();
    fc.expect(body.price).to.be.a("number");
    fc.expect(body.price).to.be.above(0);
});

Check an array is not empty

fc.test("Results list is not empty", function () {
    var body = fc.response.json();
    fc.expect(body.results).to.be.an("array");
    fc.expect(body.results).to.have.lengthOf.at.least(1);
});

Check that a value is one of a known set

fc.test("Role is valid", function () {
    var body = fc.response.json();
    fc.expect(body.role).to.be.oneOf(["admin", "editor", "viewer"]);
});

Check the body contains a specific string

fc.test("Success message is present", function () {
    fc.expect(fc.response.text()).to.include("successfully created");
});

JSON Schema Validation

Use this to catch structural changes — renamed fields, wrong types, missing required fields — without writing individual assertions for each property.

Validate a user object

fc.test("User object matches expected schema", function () {
    fc.response.to.have.jsonSchema({
        type: "object",
        required: ["id", "name", "email"],
        properties: {
            id:        { type: "integer" },
            name:      { type: "string", minLength: 1 },
            email:     { type: "string", format: "email" },
            createdAt: { type: "string" }
        }
    });
});

Validate a list response

fc.test("Paginated response matches schema", function () {
    fc.response.to.have.jsonSchema({
        type: "object",
        required: ["data", "total", "page"],
        properties: {
            data:  { type: "array" },
            total: { type: "integer", minimum: 0 },
            page:  { type: "integer", minimum: 1 }
        }
    });
});

Checking Headers

Check a header is present

fc.test("Has content type header", function () {
    fc.response.to.have.header("content-type");
});

Check a header value

fc.test("Content type is JSON", function () {
    var contentType = fc.response.headers.get("content-type");
    fc.expect(contentType).to.include("application/json");
});

Check rate limit headers

fc.test("Rate limit headers are present", function () {
    fc.response.to.have.header("x-ratelimit-limit");
    fc.response.to.have.header("x-ratelimit-remaining");
});

fc.test("Rate limit not exceeded", function () {
    var remaining = parseInt(fc.response.headers.get("x-ratelimit-remaining"));
    fc.expect(remaining).to.be.above(0);
});

Response Timing

Ensure a fast response

fc.test("Responds in under 500ms", function () {
    fc.expect(fc.response.responseTime).to.be.below(500);
});

Warn on slow response (soft limit)

fc.test("Response time is acceptable", function () {
    fc.expect(fc.response.responseTime).to.be.below(2000);
});

Authentication Flows — Chaining Requests

The most common use of test scripts: extract a token from a login response and store it so subsequent requests can use it.

Login and store access token

Put this on your login or token endpoint:

fc.test("Login succeeded", function () {
    fc.expect(fc.response.code).to.equal(200);
});

fc.test("Access token is present", function () {
    var body = fc.response.json();
    fc.expect(body.access_token).to.exist;
    fc.expect(body.access_token).to.be.a("string");
    fc.expect(body.access_token.length).to.be.above(0);

    // Store it — all subsequent requests in this run can use {{authToken}}
    fc.environment.set("authToken", body.access_token);
});

Then add Authorization: Bearer {{authToken}} to the headers of every subsequent request. The Collection Runner substitutes the value automatically.

Login and store both access and refresh token

fc.test("Tokens are present", function () {
    var body = fc.response.json();
    fc.expect(body.access_token).to.exist;
    fc.expect(body.refresh_token).to.exist;

    fc.environment.set("authToken", body.access_token);
    fc.environment.set("refreshToken", body.refresh_token);
});

Store a created resource's ID for later requests

Put this on a POST /resources endpoint:

fc.test("Resource was created", function () {
    fc.expect(fc.response.code).to.equal(201);
});

fc.test("Store new resource ID", function () {
    var id = fc.response.json().id;
    fc.expect(id).to.exist;
    fc.environment.set("resourceId", id.toString());
});

Use {{resourceId}} in the URL of subsequent GET, PUT, and DELETE requests:

GET https://api.example.com/resources/{{resourceId}}

CRUD Sequence

A full create-read-update-delete sequence. Each script stores the result so the next request can use it.

POST /users — Create:

fc.test("User created", function () {
    fc.expect(fc.response.code).to.equal(201);
    var body = fc.response.json();
    fc.expect(body.id).to.exist;
    fc.environment.set("userId", body.id.toString());
});

GET /users/ — Read:

fc.test("User found", function () {
    fc.response.to.have.status(200);
    var body = fc.response.json();
    fc.expect(body.id.toString()).to.equal(fc.environment.get("userId"));
});

PUT /users/ — Update:

fc.test("User updated", function () {
    fc.response.to.have.status(200);
    var body = fc.response.json();
    fc.expect(body.name).to.equal("Updated Name");
});

DELETE /users/ — Delete:

fc.test("User deleted", function () {
    fc.expect(fc.response.code).to.be.oneOf([200, 204]);
});

Error Handling Scenarios

Validate that a 400 includes an error message

fc.test("Error response has a message", function () {
    fc.expect(fc.response.code).to.equal(400);
    var body = fc.response.json();
    fc.expect(body.message).to.exist;
    fc.expect(body.message).to.be.a("string");
});

Validate specific validation errors

fc.test("Validation errors list specific fields", function () {
    var body = fc.response.json();
    fc.expect(body.errors).to.be.an("array");
    var fields = body.errors.map(function(e) { return e.field; });
    fc.expect(fields).to.include("email");
});

Expect 401 when no token is provided

fc.test("Returns 401 without auth", function () {
    fc.response.to.be.unauthorized;
});

fc.test("Error body has a message", function () {
    var body = fc.response.json();
    fc.expect(body.message).to.include("Unauthorized");
});

Pagination

Check a paginated list response

fc.test("Returns a page of results", function () {
    var body = fc.response.json();

    fc.expect(body.data).to.be.an("array");
    fc.expect(body.data.length).to.be.at.most(20); // max page size

    fc.expect(body.page).to.be.a("number");
    fc.expect(body.total).to.be.a("number");
});

Store the next page cursor

fc.test("Cursor is present for next page", function () {
    var body = fc.response.json();
    if (body.nextCursor) {
        fc.environment.set("nextCursor", body.nextCursor);
    }
});

Conditional Skipping and Flow Control

Stop the run if a critical step fails

If a login request fails, there is no point running the rest of the collection — all subsequent requests will fail with 401 anyway:

fc.test("Login succeeded", function () {
    if (fc.response.code !== 200) {
        fc.execution.abort();
    }
    fc.expect(fc.response.code).to.equal(200);
});

Skip cleanup if setup failed

var created = fc.environment.get("resourceId");
if (!created) {
    fc.execution.setNextRequest("End of Suite"); // jump past cleanup
}

Debugging a Failing Test

When a test is failing and you cannot see why, log the response to the Console:

console.log("Status:", fc.response.code);
console.log("Body:", fc.response.text());

var body = fc.response.json();
console.log("Parsed:", JSON.stringify(body, null, 2));

Open the Console panel (`Cmd/Ctrl + ``) to see the output. In the Collection Runner, these logs appear in the detail panel for each result row.