Skip to main content
Bruno provides two ways to validate HTTP responses:
  1. Declarative assertions - Simple assertions in the assert block
  2. 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

eq
operator
Equal to (default if no operator specified)
res.status: eq 200
res.body.name: eq John
res.body.name: John  // eq is optional
neq
operator
Not equal to
res.status: neq 404
res.body.error: neq null
gt
operator
Greater than
res.body.count: gt 0
res.body.age: gt 18
gte
operator
Greater than or equal to
res.body.score: gte 100
lt
operator
Less than
res.responseTime: lt 1000
res.body.price: lt 99.99
lte
operator
Less than or equal to
res.body.quantity: lte 100

Collection Operators

in
operator
Value is in array
res.body.status: in active,pending,completed
res.body.status: in [active, pending, completed]  // Also valid
notIn
operator
Value is not in array
res.body.status: notIn failed,error
contains
operator
String/array contains value
res.body.email: contains @example.com
res.body.tags: contains javascript
notContains
operator
String/array does not contain value
res.body.message: notContains error

String Operators

startsWith
operator
String starts with value
res.body.url: startsWith https://
res.body.name: startsWith Mr.
endsWith
operator
String ends with value
res.body.email: endsWith @example.com
res.body.filename: endsWith .pdf
matches
operator
Matches regular expression
res.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}$
notMatches
operator
Does not match regular expression
res.body.username: notMatches [^a-zA-Z0-9]

Size/Length Operators

length
operator
Array/string has exact length
res.body.items: length 10
res.body.username: length 8
between
operator
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:
isEmpty
operator
Value is empty (array, string, or object)
res.body.errors: isEmpty
res.body.message: isEmpty
isNotEmpty
operator
Value is not empty
res.body.items: isNotEmpty
isNull
operator
Value is null
res.body.deletedAt: isNull
isUndefined
operator
Value is undefined
res.body.optionalField: isUndefined
isDefined
operator
Value is defined (not undefined)
res.body.userId: isDefined
res.body.email: isDefined
isTruthy
operator
Value is truthy
res.body.isActive: isTruthy
isFalsy
operator
Value is falsy
res.body.isDeleted: isFalsy

Type Operators (Unary)

isJson
operator
Value is a JSON object
res.body.metadata: isJson
isNumber
operator
Value is a number
res.body.age: isNumber
res.body.price: isNumber
isString
operator
Value is a string
res.body.name: isString
isBoolean
operator
Value is a boolean
res.body.isActive: isBoolean
isArray
operator
Value is an array
res.body.items: isArray

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)
function
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');
    });
  });
}

Header Validation

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

Build docs developers (and LLMs) love