coldbox-testing-handler
Use this skill when testing ColdBox event handlers with execute(), asserting rc/prc collections, verifying view selection and rendered output, mocking relocations, testing renderData() and getHandlerResults(), setting HTTP methods and headers, injecting mocks into handlers, or using BaseHandlerTest for isolated handler unit tests.
Handler Testing in ColdBox
When to Use This Skill
- Writing tests for ColdBox handlers/controllers
- Asserting what view, layout, or data a handler returns
- Verifying redirect/relocation behavior after form submissions
- Testing RESTful handlers that call
renderData() - Checking HTTP status codes set by handlers
- Unit-testing a handler in full isolation with
BaseHandlerTest
Language Mode Reference
| Concept | BoxLang (.bx) preferred | CFML (.cfc) compatible |
|---|---|---|
| Class declaration | class extends="..." {} | component extends="..." {} |
| Closures | () => {} | function() {} |
Testing Class Choices
| Class | Purpose |
|---|---|
coldbox.system.testing.BaseTestCase | Integration โ loads the full virtual ColdBox app |
coldbox.system.testing.BaseHandlerTest | Isolated unit โ tests the handler CFC with no app load |
Use BaseTestCase for most handler tests. Use BaseHandlerTest only when you want full isolation.
Integration Handler Test (BaseTestCase)
class extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {
function beforeAll() {
super.beforeAll()
}
function afterAll() {
super.afterAll()
}
function run() {
describe( "Main Handler", () => {
beforeEach( () => {
// CRITICAL: fresh virtual request per spec
setup()
} )
it( "renders the homepage", () => {
var event = execute( event = "main.index", renderResults = true )
expect( event.getPrivateValue( "welcomeMessage" ) ).toBe( "Welcome to ColdBox!" )
expect( event.getCurrentView() ).toBe( "main/index" )
} )
it( "places users in prc", () => {
var event = execute( event = "users.index", renderResults = true )
expect( event.getPrivateValue( "users" ) ).toBeArray()
} )
it( "stores a user and redirects", () => {
var event = execute(
event = "users.store",
renderResults = true,
eventArguments = { name: "Alice", email: "[email protected]" }
)
expect( event.getValue( "relocate_event", "" ) ).toBe( "users.index" )
} )
} )
}
}
The execute() Method
execute() simulates a ColdBox event and returns the request context. It is limited to GET-style simulation. For POST/PUT/DELETE use the HTTP method helpers.
| Argument | Type | Default | Description |
|---|---|---|---|
event | string | โ | Event to execute e.g. "users.index" |
route | string | โ | Route URL e.g. "/users" โ also exercises URL mappings |
queryString | string | "" | Query string appended to the simulated request |
private | boolean | false | Execute as a private event |
prePostExempt | boolean | false | Skip pre/post interceptors |
eventArguments | struct | {} | Arguments passed directly into the handler action |
renderResults | boolean | false | Render output and store in cbox_rendered_content |
withExceptionHandling | boolean | false | Route errors through ColdBox exception handling |
domain | string | cgi.server_name | Simulate a specific domain |
Asserting Results
Handler Return Value
// handler
function list( event, rc, prc ) {
return "Hola Luis"
}
// spec
var event = execute( event = "main.list", renderResults = true )
expect( event.getHandlerResults() ).toBe( "Hola Luis" )
// also available as rc value
expect( event.getValue( "cbox_handler_results" ) ).toBe( "Hola Luis" )
View Selection
var event = execute( event = "users.index", renderResults = true )
expect( event.getCurrentView() ).toBe( "users/index" )
Rendered Content
var event = execute( event = "main.data", renderResults = true )
expect( event.getRenderedContent() ).toBeJSON()
renderData() โ REST handlers
// handler
function data( event, rc, prc ) {
event.renderData( data = myService.getData(), type = "json" )
}
// spec
var event = execute( event = "main.data", renderResults = true )
expect( event.getRenderData().type ).toBe( "json" )
expect( event.getPrivateValue( "cbox_renderdata" ).type ).toBe( "json" )
HTTP Status Code
var event = execute( event = "api.users.show", renderResults = true )
expect( event.getStatusCode() ).toBe( 200 )
// ColdBox matcher shorthand
expect( event.getResponse() ).toHaveStatus( 200 )
prc / rc Values
// private collection (set by handler via prc.x = ...)
var user = event.getPrivateValue( "user" )
expect( user ).toHaveKey( "id" )
expect( user.email ).toBe( "[email protected]" )
// public collection (from rc)
var name = event.getValue( "name" )
Mocking Relocations
ColdBox automatically stubs relocate() during tests. Relocation arguments are saved as relocate_* keys in the rc.
it( "redirects to users.index after save", () => {
var event = execute(
event = "users.store",
eventArguments = { name: "Bob", email: "[email protected]" }
)
expect( event.getValue( "relocate_event", "" ) ).toBe( "users.index" )
} )
it( "redirects with status 301", () => {
var event = execute( event = "legacy.redirect" )
expect( event.getValue( "relocate_statusCode", "" ) ).toBe( "301" )
} )
The available relocate_* keys:
| Key | Description |
|---|---|
relocate_event | Target ColdBox event |
relocate_URL | Target URL |
relocate_URI | Target URI |
relocate_queryString | Query string |
relocate_statusCode | HTTP redirect status code |
Or use the ColdBox TestBox matcher:
expect( event ).toRedirectTo( "main.index" )
Lifecycle and setup()
function beforeAll() {
super.beforeAll()
// one-time app-level setup
}
function afterAll() {
super.afterAll()
}
function run() {
describe( "Users Handler", () => {
beforeEach( () => {
setup() // fresh virtual request per spec โ never skip this
} )
// specs...
} )
}
Important: If you do not call
super.beforeAll()/super.afterAll(), the virtual app will not load/unload correctly. Always funnel the super call.
Performance: Keep ColdBox Loaded Across Bundles
By default the virtual app is destroyed after each bundle. To share it across bundles for faster runs:
class extends="coldbox.system.testing.BaseTestCase"
appMapping="/root"
unloadColdBox="false"
{
this.unloadColdBox = false
// ...
}
Test Annotations
| Annotation | Default | Description |
|---|---|---|
appMapping | / | Slash-notation path to the ColdBox app root |
configMapping | {appMapping}/config/Coldbox.cfc | Dot-notation path to config CFC |
coldboxAppKey | cbController | Application scope key for the controller |
loadColdBox | true | Whether to load the virtual app |
unloadColdBox | true | Whether to unload the virtual app after specs |
class extends="coldbox.system.testing.BaseTestCase"
appMapping="/apps/MyApp"
configMapping="apps.MyApp.test.resources.Config"
{}
Injecting Mock Services
beforeEach( () => {
setup()
variables.mockUserService = createMock( "models.UserService" )
prepareMock( getInstance( "users@handlers" ) )
.$property( propertyName = "userService", mock = mockUserService )
} )
it( "calls userService.findAll", () => {
mockUserService.$( "findAll" ).$results( [ { id: 1, name: "Alice" } ] )
var event = execute( event = "users.index", renderResults = true )
expect( mockUserService.$once( "findAll" ) ).toBeTrue()
expect( event.getPrivateValue( "users" ) ).toHaveLength( 1 )
} )
Isolated Handler Unit Test (BaseHandlerTest)
BaseHandlerTest tests the handler CFC in complete isolation โ no virtual ColdBox app. Mock all dependencies manually.
class extends="coldbox.system.testing.BaseHandlerTest" handler="handlers.users" {
function beforeAll() {
super.setup()
mockUserService = createMock( "models.UserService" )
handler.$property( propertyName = "userService", mock = mockUserService )
}
function run() {
describe( "users handler โ isolated", () => {
it( "index places users in prc", () => {
mockUserService.$( "findAll" ).$results( [ { id: 1 } ] )
var event = execute( event = "users.index" )
expect( event.getPrivateValue( "users" ) ).toHaveLength( 1 )
} )
} )
}
}
Integration tests load the full ColdBox app; isolated handler tests (
BaseHandlerTest) do not. In isolated tests you must mock every dependency the handler uses.
CommandBox Scaffolding
coldbox create integration-test handler=users actions=index,show,store --open
Key Methods Quick Reference
| Method | Description |
|---|---|
execute( event, renderResults, eventArguments ) | Execute a ColdBox event |
setup() | Reset the virtual request โ call in beforeEach |
event.getCurrentView() | View name chosen by the handler |
event.getRenderedContent() | Rendered output string |
event.getHandlerResults() | Value explicitly returned by the handler action |
event.getRenderData() | Struct from renderData() call |
event.getPrivateValue( key ) | Read from prc |
event.getValue( key ) | Read from rc |
event.getStatusCode() | HTTP status code |
event.getResponse() | ColdBox response object (supports .toHaveStatus()) |
prepareMock( handler ) | Prepare handler for property injection |
getInstance( name ) | Get a WireBox-managed object |