๐Ÿ”ท Core

coldbox-testing-interceptor

Use this skill when unit testing ColdBox interceptors in isolation using BaseInterceptorTest, accessing the pre-wired interceptor variable, leveraging mock helpers (mockController, mockRequestService, mockLogger, mockLogBox, mockFlash), configuring interceptor properties with configProperties, calling interceptor announce points directly, or scaffolding interceptor tests with CommandBox.

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

Interceptor Unit Testing in ColdBox

When to Use This Skill

  • Unit testing an interceptor CFC in complete isolation (no virtual ColdBox app)
  • Testing individual interception points (preProcess, postProcess, onException, custom events)
  • Mocking the controller, request service, logger, or flash scope
  • Verifying that an interceptor modifies the request context or calls other services

Base Class

coldbox.system.testing.BaseInterceptorTest

Extends BaseTestCase with loadColdBox="false". Your interceptor CFC is instantiated and handed to you in variables.interceptor.


Minimal Setup

class extends="coldbox.system.testing.BaseInterceptorTest"
    interceptor="interceptors.SecurityFirewall"
{

    function beforeAll() {
        super.setup()   // creates variables.interceptor and all mock helpers
    }

    function run() {
        describe( "SecurityFirewall", () => {

            it( "allows authenticated requests through", () => {
                // inject a mock authenticated session into the mocked request context
                mockRequestContext.$( "getValue" ).$results( { userId: 42 } )

                interceptor.preProcess( mockRequestContext, {} )

                expect( mockRequestContext.$never( "noRender" ) ).toBeTrue()
            } )

        } )
    }
}

Pre-Wired Mock Variables

After super.setup(), the following are available:

VariableDescription
variables.interceptorThe instantiated interceptor under test
variables.mockControllerMock of the ColdBox controller
variables.mockRequestServiceMock of the request service
variables.mockLoggerMock LogBox logger
variables.mockLogBoxMock LogBox instance
variables.mockFlashMock flash scope

Calling Interception Points

Invoke any interception point directly as a method on interceptor:

interceptor.preProcess( mockEvent, interceptData )
interceptor.postProcess( mockEvent, interceptData )
interceptor.onException( mockEvent, { exception: cfcatch } )
interceptor.preHandlerInvoke( mockEvent, interceptData )
interceptor.postHandlerInvoke( mockEvent, interceptData )

// Custom announce point registered by the interceptor
interceptor.onUserLogin( mockEvent, { user: { id: 1 } } )

Configuring Interceptor Properties

Many interceptors read their own config properties. Set them via configProperties before calling super.setup():

class extends="coldbox.system.testing.BaseInterceptorTest"
    interceptor="interceptors.RateLimiter"
{

    function beforeAll() {
        variables.configProperties = {
            maxRequests : 100,
            windowSeconds : 60
        }
        super.setup()
    }

    function run() {
        describe( "RateLimiter", () => {
            it( "blocks requests beyond the limit", () => {
                // test with configProperties already applied
            } )
        } )
    }
}

Full Example โ€” Security Interceptor

class extends="coldbox.system.testing.BaseInterceptorTest"
    interceptor="interceptors.SecurityFirewall"
{

    function beforeAll() {
        super.setup()
        // Inject mock services the interceptor has via WireBox
        variables.mockJWTService = createMock( "cbsecurity.models.JWTService" )
        prepareMock( interceptor )
            .$property( propertyName = "jwtService", mock = mockJWTService )
        interceptor.configure()
    }

    function run() {

        describe( "SecurityFirewall interceptor", () => {

            describe( "preProcess", () => {

                it( "allows request when valid JWT present", () => {
                    mockJWTService
                        .$( "parseToken" )
                        .$results( { valid: true, userId: 42 } )
                    mockRequestContext
                        .$( "getHTTPHeader" )
                        .$results( "Bearer valid.jwt.token" )
                    mockRequestContext.$( "setValue" )

                    interceptor.preProcess( mockRequestContext, {} )

                    expect( mockRequestContext.$once( "setValue", [ "currentUser", { valid: true, userId: 42 } ] ) ).toBeTrue()
                } )

                it( "aborts request when token is invalid", () => {
                    mockJWTService
                        .$( "parseToken" )
                        .$throws( type = "TokenInvalidException" )
                    mockRequestContext
                        .$( "getHTTPHeader" )
                        .$results( "" )
                    mockRequestContext.$( "noRender" )

                    interceptor.preProcess( mockRequestContext, {} )

                    expect( mockRequestContext.$once( "noRender" ) ).toBeTrue()
                } )

            } )

        } )
    }
}

Mocking Additional Dependencies

function beforeAll() {
    super.setup()

    // Inject any additional mock collaborators
    variables.mockCacheService = createMock( "models.CacheService" )
    prepareMock( interceptor )
        .$property( propertyName = "cacheService",  mock = mockCacheService )
        .$property( propertyName = "log",            mock = mockLogger )
        .$property( propertyName = "controller",     mock = mockController )

    interceptor.configure()
}

Testing Announce Points on the Controller

it( "announces a custom event after processing", () => {
    mockController.$( "announce" )

    interceptor.postHandlerInvoke( mockRequestContext, {} )

    expect( mockController.$once( "announce", [ "onAfterHandlerInvoke", {} ] ) ).toBeTrue()
} )

Testing Flash Scope Interactions

it( "stores messages in flash scope", () => {
    mockFlash.$( "put" )
    mockRequestContext.$( "getPrivateValue" ).$results( "Access denied" )

    interceptor.preProcess( mockRequestContext, {} )

    expect( mockFlash.$once( "put" ) ).toBeTrue()
} )

CommandBox Scaffolding

# Generate an interceptor and its unit test
coldbox create interceptor name=SecurityFirewall points=preProcess,postProcess --tests

# Or generate just the test
coldbox create unit-test name=SecurityFirewall type=interceptor --open

Key Patterns Summary

PatternCode
Extend base classextends="coldbox.system.testing.BaseInterceptorTest" interceptor="interceptors.MyInterceptor"
Boot interceptorsuper.setup() then interceptor.configure() (if applicable)
Set config propertiesAssign variables.configProperties = {} BEFORE super.setup()
Inject mock loggerprepareMock( interceptor ).$property( propertyName="log", mock=mockLogger )
Mock collaboratorcreateMock("models.X"), inject via $property()
Call announce pointinterceptor.preProcess( mockRequestContext, interceptData )
Assert method calledexpect( mockX.$once("method") ).toBeTrue()
Assert method NOT calledexpect( mockX.$never("method") ).toBeTrue()