Documentation Index
Fetch the complete documentation index at: https://mintlify.com/usebruno/bruno/llms.txt
Use this file to discover all available pages before exploring further.
Bruno provides two ways to validate HTTP responses:
- Declarative assertions - Simple assertions in the
assert block
- Test scripts - JavaScript test functions in the
tests block
Declarative Assertions
The assert block provides a simple, declarative syntax for common assertions:
assert {
res.status: eq 200
res.body.userId: isDefined
res.body.email: contains @example.com
}
Assertion Operators
Assertions use the format: expression: operator value
Comparison Operators
Equal to (default if no operator specified)res.status: eq 200
res.body.name: eq John
res.body.name: John // eq is optional
Not equal tores.status: neq 404
res.body.error: neq null
Greater thanres.body.count: gt 0
res.body.age: gt 18
Less thanres.responseTime: lt 1000
res.body.price: lt 99.99
Less than or equal tores.body.quantity: lte 100
Collection Operators
Value is in arrayres.body.status: in active,pending,completed
res.body.status: in [active, pending, completed] // Also valid
Value is not in arrayres.body.status: notIn failed,error
String/array contains valueres.body.email: contains @example.com
res.body.tags: contains javascript
String/array does not contain valueres.body.message: notContains error
String Operators
String starts with valueres.body.url: startsWith https://
res.body.name: startsWith Mr.
String ends with valueres.body.email: endsWith @example.com
res.body.filename: endsWith .pdf
Matches regular expressionres.body.email: matches ^[a-z]+@[a-z]+\\.[a-z]+$
res.body.email: matches /^[a-z]+@/ // Slashes optional
res.body.phone: matches ^\\d{3}-\\d{3}-\\d{4}$
Does not match regular expressionres.body.username: notMatches [^a-zA-Z0-9]
Size/Length Operators
Array/string has exact lengthres.body.items: length 10
res.body.username: length 8
Value is between two numbers (inclusive)res.body.age: between 18, 65
res.responseTime: between 100, 500
State Operators (Unary)
These operators don’t require a value:
Value is empty (array, string, or object)res.body.errors: isEmpty
res.body.message: isEmpty
Value is not emptyres.body.items: isNotEmpty
Value is nullres.body.deletedAt: isNull
Value is undefinedres.body.optionalField: isUndefined
Value is defined (not undefined)res.body.userId: isDefined
res.body.email: isDefined
Value is truthyres.body.isActive: isTruthy
Value is falsyres.body.isDeleted: isFalsy
Type Operators (Unary)
Value is a JSON objectres.body.metadata: isJson
Value is a numberres.body.age: isNumber
res.body.price: isNumber
Value is a booleanres.body.isActive: isBoolean
Assertion Examples
assert {
// Status assertions
res.status: eq 200
res.statusText: eq OK
// Response time
res.responseTime: lt 1000
// Header assertions
res.headers['content-type']: contains application/json
// Body assertions
res.body.success: eq true
res.body.userId: isDefined
res.body.userId: isNumber
res.body.email: matches ^[^@]+@[^@]+\\.[^@]+$
res.body.items: isArray
res.body.items: isNotEmpty
res.body.items: length 10
res.body.status: in active,pending
// Nested properties
res.body.user.name: eq John Doe
res.body.user.age: gt 18
res.body.user.email: endsWith @example.com
// Array elements
res.body.items[0].id: isDefined
res.body.items[0].title: isString
}
Test Scripts
For more complex validations, use the tests block with JavaScript:
tests {
test("description", function() {
// Test code with assertions
});
}
Test Function
test(description, callback)
Defines a test caseParameters:
description (string) - Test description
callback (function) - Test function (can be async)
test("should return valid user data", function() {
const body = res.getBody();
expect(body).to.have.property('userId');
expect(body.userId).to.be.a('number');
});
// Async test
test("should validate async operation", async function() {
await someAsyncOperation();
expect(result).to.be.true;
});
Chai Assertions
Bruno includes the Chai assertion library with BDD-style assertions.
Expect API
The expect function is the primary assertion method:
test("equality assertions", function() {
expect(res.getStatus()).to.equal(200);
expect(res.getBody().name).to.equal('John');
expect(res.getBody()).to.deep.equal({ userId: 1, name: 'John' });
expect(res.getStatus()).to.not.equal(404);
});
Assert API
Alternatively, use the assert style:
test("using assert style", function() {
assert.equal(res.getStatus(), 200);
assert.isNumber(res.getBody().userId);
assert.isString(res.getBody().name);
assert.isArray(res.getBody().items);
assert.isTrue(res.getBody().success);
assert.isDefined(res.getBody().userId);
assert.isNull(res.getBody().deletedAt);
});
Chaining Assertions
Chain multiple assertions for better readability:
test("chained assertions", function() {
expect(res.getBody().items)
.to.be.an('array')
.that.is.not.empty
.and.has.lengthOf(10);
expect(res.getBody().user)
.to.have.property('name')
.that.is.a('string')
.and.equals('John Doe');
});
Common Test Patterns
Status Code Validation
tests {
test("should return 200 OK", function() {
expect(res.getStatus()).to.equal(200);
});
test("should be successful status", function() {
expect(res.getStatus()).to.be.within(200, 299);
});
}
Response Body Validation
tests {
test("should return user object", function() {
const body = res.getBody();
expect(body).to.have.all.keys('userId', 'name', 'email', 'createdAt');
expect(body.userId).to.be.a('number');
expect(body.name).to.be.a('string').and.not.be.empty;
expect(body.email).to.match(/^[^@]+@[^@]+\.[^@]+$/);
});
}
Array Validation
tests {
test("should return array of items", function() {
const items = res.getBody().items;
expect(items).to.be.an('array');
expect(items).to.have.lengthOf.at.least(1);
// Validate first item
expect(items[0]).to.have.property('id');
expect(items[0]).to.have.property('title');
// Validate all items
items.forEach(item => {
expect(item.id).to.be.a('number');
expect(item.title).to.be.a('string');
});
});
}
tests {
test("should have correct headers", function() {
const headers = res.getHeaders();
expect(headers).to.have.property('content-type');
expect(headers['content-type']).to.include('application/json');
expect(res.getHeader('x-rate-limit-remaining')).to.be.a('string');
});
}
Response Time Validation
tests {
test("should respond quickly", function() {
expect(res.getResponseTime()).to.be.lessThan(1000);
});
}
JWT Token Validation
tests {
const atob = require('atob');
test("should return valid JWT", function() {
const token = res.getBody().token;
expect(token).to.be.a('string');
const parts = token.split('.');
expect(parts).to.have.lengthOf(3);
// Decode and validate payload
const payload = JSON.parse(atob(parts[1]));
expect(payload).to.have.property('userId');
expect(payload).to.have.property('exp');
expect(payload.exp).to.be.greaterThan(Date.now() / 1000);
});
}
Schema Validation
tests {
const tv4 = require('tv4');
test("should match schema", function() {
const schema = {
type: 'object',
required: ['userId', 'name', 'email'],
properties: {
userId: { type: 'number' },
name: { type: 'string' },
email: { type: 'string', format: 'email' }
}
};
const valid = tv4.validate(res.getBody(), schema);
expect(valid).to.be.true;
if (!valid) {
console.error('Validation error:', tv4.error);
}
});
}
Combining Assertions and Tests
You can use both declarative assertions and test scripts together:
assert {
res.status: eq 200
res.body.userId: isDefined
}
tests {
test("should have valid email format", function() {
expect(res.getBody().email).to.match(/^[^@]+@[^@]+$/);
});
test("should have future expiration", function() {
const expiresAt = new Date(res.getBody().expiresAt);
expect(expiresAt).to.be.greaterThan(new Date());
});
}
Next Steps
Pre-request Scripts
Modify requests before sending
Post-response Scripts
Process responses and extract data