๐Ÿ”ท Core

coldbox-testing-integration

Use this skill when writing integration tests for ColdBox that use real dependencies (database, WireBox, ColdBox context), testing full request/response cycles with execute(), setting up and tearing down test databases, testing services with real data, or using BaseTestCase for end-to-end handler tests with actual WireBox injections.

$ npx skills add coldbox/skills/coldbox/testing-integration
$ coldbox ai skills install coldbox/skills/coldbox/testing-integration
๐Ÿ”— https://skills.boxlang.io/skills/raw/coldbox/skills/coldbox~testing-integration

Integration Testing in ColdBox

Overview

Integration tests verify how components work together using real dependencies โ€” actual database connections, real WireBox injections, and full ColdBox request cycles. Unlike unit tests, integration tests do not mock external dependencies, instead relying on a test database or real services.

Language Mode Reference

Examples use BoxLang (.bx) syntax by default. Adapt for your target language:

ConceptBoxLang (.bx)CFML (.cfc)
Class declarationclass [extends="..."] {component [extends="..."] {
DI annotation@inject above property name="svc";property name="svc" inject="svc";
View templates.bxm suffix.cfm / .cfml suffix
Tag prefix<bx:if>, <bx:output>, <bx:set><cfif>, <cfoutput>, <cfset>

CFML Compat Mode: With BoxLang + CFML Compat module, .bx and .cfc files coexist freely. BoxLang-native classes use class {} (.bx files); CFML-compat classes use component {} (.cfc files).

Basic Integration Test

component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {

    function beforeAll() {
        super.setup()
        // Get real services from WireBox (no mocks)
        variables.userService = getInstance( "UserService" )
    }

    function afterAll() {
        structDelete( application, "cbController" )
    }

    function run() {
        describe( "UserService Integration", () => {

            beforeEach( () => {
                setupTestDatabase()
            } )

            afterEach( () => {
                cleanupTestDatabase()
            } )

            it( "should create and retrieve a user from DB", () => {
                // Act - uses real database
                user = userService.create( {
                    name: "Alice Smith",
                    email: "[email protected]"
                } )

                // Assert
                expect( user.id ).toBeNumeric()

                // Retrieve from DB
                found = userService.findById( user.id )
                expect( found.name ).toBe( "Alice Smith" )
            } )

            it( "should return empty when no users exist", () => {
                users = userService.findAll()
                expect( users ).toBeArray()
                expect( users ).toBeEmpty()
            } )
        } )
    }

    private function setupTestDatabase() {
        queryExecute( "DELETE FROM users WHERE email LIKE '%@testexample.com'" )
    }

    private function cleanupTestDatabase() {
        queryExecute( "DELETE FROM users WHERE email LIKE '%@testexample.com'" )
    }
}

Full Request Cycle Integration Test

component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {

    function beforeAll() {
        super.setup()
    }

    function run() {
        describe( "User Registration Integration", () => {

            beforeEach( () => {
                queryExecute( "DELETE FROM users WHERE email = '[email protected]'" )
            } )

            it( "should register a user through the full request cycle", () => {
                // Execute through full ColdBox handler stack
                event = execute(
                    event = "users.register",
                    renderResults = true,
                    eventArguments = {
                        name: "Integration User",
                        email: "[email protected]",
                        password: "TestPass123!"
                    }
                )

                expect( event.getStatusCode() ).toBe( 201 )

                // Verify user actually persisted in DB
                result = queryExecute(
                    "SELECT * FROM users WHERE email = :email",
                    { email: "[email protected]" }
                )

                expect( result.recordCount ).toBe( 1 )
            } )
        } )
    }
}

Database Transaction Rollback Pattern

component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {

    function beforeAll() {
        super.setup()
        variables.userService = getInstance( "UserService" )
    }

    function run() {
        describe( "Database integration with rollback", () => {

            beforeEach( () => {
                // Start a transaction before each test
                transaction action="begin" {}
            } )

            afterEach( () => {
                // Roll back all changes โ€” keeps DB clean
                transaction action="rollback" {}
            } )

            it( "should create a user (rolled back after)", () => {
                user = userService.create( {
                    name: "Transaction User",
                    email: "[email protected]"
                } )

                expect( user.id ).toBeNumeric()

                // Verify within transaction
                found = userService.findById( user.id )
                expect( found.name ).toBe( "Transaction User" )
            } )
        } )
    }
}

Multi-Service Integration Test

component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {

    function beforeAll() {
        super.setup()
        // Get all real services
        variables.userService  = getInstance( "UserService" )
        variables.orderService = getInstance( "OrderService" )
        variables.emailService = getInstance( "EmailService" )
    }

    function run() {
        describe( "Order placement integration", () => {

            it( "should create user and place first order", () => {
                // Create real user in DB
                user = userService.create( {
                    name: "New Customer",
                    email: "[email protected]"
                } )

                // Place order for that user
                order = orderService.placeOrder( {
                    customerId: user.id,
                    items: [ { productId: 1, qty: 1, price: 9.99 } ]
                } )

                // Verify order persisted
                expect( order.id ).toBeNumeric()
                expect( order.customerId ).toBe( user.id )
                expect( order.status ).toBe( "pending" )

                // Verify email was queued
                emails = emailService.getQueuedEmails( user.id )
                expect( emails ).toHaveLength( 1 )
                expect( emails[1].type ).toBe( "order_confirmation" )
            } )
        } )
    }
}

API Integration Test (Full Stack)

component extends="coldbox.system.testing.BaseTestCase" appMapping="/root" {

    function beforeAll() {
        super.setup()
        variables.authToken = loginAsTestUser()
    }

    function run() {
        describe( "API Integration Tests", () => {

            it( "should return paginated user list", () => {
                event = execute(
                    event = "api.v1.users.index",
                    renderResults = true
                )

                expect( event.getStatusCode() ).toBe( 200 )

                response = deserializeJSON( event.getRenderedContent() )
                expect( response ).toHaveKey( "data" )
                expect( response ).toHaveKey( "pagination" )
                expect( response.pagination ).toHaveKey( "total" )
            } )

            it( "should reject unauthenticated request", () => {
                event = execute(
                    event = "api.v1.admin.users",
                    renderResults = true
                )

                expect( event.getStatusCode() ).toBe( 401 )
            } )
        } )
    }

    private function loginAsTestUser() {
        loginEvent = execute(
            event = "api.v1.auth.login",
            renderResults = true,
            eventArguments = {
                email: "[email protected]",
                password: "testpassword"
            }
        )
        response = deserializeJSON( loginEvent.getRenderedContent() )
        return response.token
    }
}

Integration vs Unit Test Decision

ScenarioUse Unit TestUse Integration Test
Testing business logicโœ…โ€”
Testing DB persistenceโ€”โœ…
Testing handler routingโ€”โœ…
Testing service interactionsMocks OKโœ… Real
Testing external APIsMockโœ… Sandbox
Fast feedback loopโœ…โ€”
Full stack validationโ€”โœ…

Best Practices

  1. Isolate test data โ€” use unique emails like test+{uuid}@example.com or transactions with rollback
  2. Use dedicated test datasource โ€” configure a test DB in Application.cfc
  3. Seed minimal data โ€” only create what the test needs
  4. Run integration tests separately โ€” tag with labels="integration" to run independently from unit tests
  5. Keep tests deterministic โ€” don't rely on existing DB state; set it up explicitly