Documentation Index
Fetch the complete documentation index at: https://docs.usebruno.com/llms.txt
Use this file to discover all available pages before exploring further.
Bruno uses the Chai library ↗ for assertions. All Chai expect syntax works in Bruno tests.
Write JavaScript test scripts to validate API responses, handle complex logic, and automate testing workflows.
Basic Test Structure
test("test name", function () {
expect(res.getStatus()).to.equal(200);
});
Common Test Examples
Testing Status Codes
test("should return success", function () {
expect(res.getStatus()).to.equal(200);
});
test("should not return server error", function () {
expect(res.getStatus()).to.not.equal(500);
});
Testing Response Body
test("should return user data", function () {
const body = res.getBody();
expect(body).to.have.property("id");
expect(body.name).to.equal("John Doe");
expect(body.email).to.contain("@example.com");
});
test("should return array of users", function () {
const users = res.getBody();
expect(users).to.be.an("array");
expect(users).to.have.lengthOf(3);
expect(users[0]).to.have.property("id");
});
Validating the JSON Body with jsonBody
Bruno provides a custom Chai assertion jsonBody to make response-body checks more readable. It’s the native Bruno equivalent of Postman’s pm.response.to.have.jsonBody(...), and supports four call shapes:
test("body is JSON", function () {
expect(res.getBody()).to.have.jsonBody();
});
test("body deep-equals an object", function () {
expect(res.getBody()).to.have.jsonBody({ id: 1, name: "Alice" });
});
test("a nested path exists", function () {
expect(res.getBody()).to.have.jsonBody("user.id");
});
test("a nested path equals a value", function () {
expect(res.getBody()).to.have.jsonBody("user.id", 123);
});
Negation is supported across all variants:
test("body is not the legacy shape", function () {
expect(res.getBody()).to.not.have.jsonBody({ legacy: true });
});
test("debug field is not exposed", function () {
expect(res.getBody()).to.not.have.jsonBody("internal.debugInfo");
});
Migrating from Postman? When you import a Postman collection, Bruno automatically translates pm.response.to.have.jsonBody(...) (and pm.response.to.not.have.jsonBody(...)) into expect(res.getBody()).to.have.jsonBody(...). No manual rewriting needed.
Validating against a JSON Schema with jsonSchema
For contract testing, use the jsonSchema assertion to validate a response against a JSON Schema document. Bruno uses Ajv under the hood, so all modern JSON Schema drafts are supported (04, 06, 07, 2019-09, 2020-12) along with the full ajv-formats library.
test("response matches the user schema", function () {
const userSchema = {
type: "object",
required: ["id", "email"],
properties: {
id: { type: "integer" },
email: { type: "string", format: "email" },
createdAt: { type: "string", format: "date-time" },
website: { type: "string", format: "uri" }
}
};
expect(res.getBody()).to.have.jsonSchema(userSchema);
});
Common ajv-formats validators are available out of the box, including email, date, date-time, time, uri, uri-reference, uuid, ipv4, ipv6, hostname, and regex.
test("user has a valid email and signup date", function () {
expect(res.getBody()).to.have.jsonSchema({
type: "object",
properties: {
email: { type: "string", format: "email" },
signupDate: { type: "string", format: "date-time" }
}
});
});
Passing Ajv options
You can pass any Ajv options as a second argument — useful, for example, to collect every validation error rather than failing on the first one:
test("collect every schema violation", function () {
expect(res.getBody()).to.have.jsonSchema(userSchema, { allErrors: true });
});
Negation
test("response should not match the legacy v1 schema", function () {
expect(res.getBody()).to.not.have.jsonSchema(legacyV1Schema);
});
Like jsonBody, the jsonSchema assertion runs in both the Safe Mode and the Developer Mode, and Postman’s pm.response.to.have.jsonSchema(schema) is automatically translated to expect(res.getBody()).to.have.jsonSchema(schema) on import.
Earlier versions of Bruno used tv4 for schema validation, which only supports JSON Schema draft-04. The new Ajv-based jsonSchema assertion is the recommended way to validate schemas going forward and supports drafts 04, 06, 07, 2019-09, and 2020-12.
Testing Nested Objects
test("should validate nested user profile", function () {
const body = res.getBody();
expect(body.user.profile.name).to.equal("Alice");
expect(body.user.settings.theme).to.equal("dark");
expect(body.user.settings.notifications).to.be.true;
});
Testing with Conditional Logic
test("should validate response based on status", function () {
const status = res.getStatus();
const body = res.getBody();
if (status === 200) {
expect(body).to.have.property("data");
expect(body.data).to.not.be.empty;
} else if (status === 404) {
expect(body).to.have.property("error");
expect(body.error.message).to.contain("not found");
} else {
throw new Error(`Unexpected status code: ${status}`);
}
});
test("should validate user role permissions", function () {
const body = res.getBody();
if (body.user.role === "admin") {
expect(body.user.permissions).to.include("write");
expect(body.user.permissions).to.include("delete");
} else if (body.user.role === "user") {
expect(body.user.permissions).to.include("read");
expect(body.user.permissions).to.not.include("delete");
}
});
Testing Arrays and Loops
test("should validate all users have required fields", function () {
const users = res.getBody();
users.forEach((user) => {
expect(user).to.have.property("id");
expect(user).to.have.property("email");
expect(user.email).to.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
});
});
test("should find user by id", function () {
const users = res.getBody();
const targetUser = users.find((u) => u.id === 123);
expect(targetUser).to.exist;
expect(targetUser.name).to.equal("John");
});
test("should have correct content type", function () {
expect(res.getHeader("content-type")).to.contain("application/json");
});
test("should include authentication headers", function () {
expect(res.getHeader("x-api-key")).to.exist;
expect(res.getHeader("authorization")).to.not.be.empty;
});
Testing Response Time
test("should respond quickly", function () {
expect(res.getResponseTime()).to.be.lessThan(1000);
});
Advanced: Saving Values for Next Request
test("should save token for next request", function () {
const body = res.getBody();
expect(body).to.have.property("token");
expect(body.token).to.not.be.empty;
// Save token to environment variable
bru.setVar("authToken", body.token);
});
test("should extract and save user ID", function () {
const body = res.getBody();
if (body.users && body.users.length > 0) {
const firstUserId = body.users[0].id;
bru.setVar("userId", firstUserId);
}
});
Error Handling
test("should handle missing fields gracefully", function () {
const body = res.getBody();
if (!body.data) {
expect(body).to.have.property("error");
expect(body.error.code).to.be.oneOf([400, 404, 422]);
}
});
test("should validate error response structure", function () {
const status = res.getStatus();
if (status >= 400) {
const body = res.getBody();
expect(body).to.have.property("error");
expect(body.error).to.have.property("message");
expect(body.error.message).to.be.a("string");
}
});
Common Chai Assertions
// Equality
expect(value).to.equal(expected);
expect(value).to.not.equal(expected);
expect(value).to.eql(expected); // deep equality
// Type checking
expect(value).to.be.a("string");
expect(value).to.be.an("array");
expect(value).to.be.true;
expect(value).to.be.null;
// Property checks
expect(obj).to.have.property("key");
expect(obj).to.have.all.keys("name", "email");
// String checks
expect(str).to.contain("substring");
expect(str).to.match(/regex/);
// Number comparisons
expect(num).to.be.above(10);
expect(num).to.be.below(100);
expect(num).to.be.within(10, 100);
// Array checks
expect(arr).to.be.an("array");
expect(arr).to.have.lengthOf(3);
expect(arr).to.include("item");
expect(arr).to.be.empty;
// Bruno-specific: JSON body and schema validation
expect(res.getBody()).to.have.jsonBody(); // body is JSON
expect(res.getBody()).to.have.jsonBody({ id: 1 }); // deep-equals
expect(res.getBody()).to.have.jsonBody("user.id"); // path exists
expect(res.getBody()).to.have.jsonBody("user.id", 123); // path equals value
expect(res.getBody()).to.have.jsonSchema(schema); // validate via Ajv
expect(res.getBody()).to.have.jsonSchema(schema, { allErrors: true });
Next Steps
For more advanced scripting capabilities, see: