Unit
Use this skill when writing xUnit-style tests in TestBox using test functions (testXxx()), setup/teardown lifecycle (beforeTests/afterTests/setup/teardown), $assert assertion object, or the Arrange-Act-Assert (AAA) pattern for unit testing services, models, and utilities in isolation.From its SKILL.md
npx -y skills add ColdBox/skills --skill unitAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
8.1 KB, ~2.0k tokens by cl100k_base, as published. Nobody here has run it
xUnit / Unit Testing with TestBox
When to Use This Skill
- Writing xUnit-style test bundles (functions prefixed with
test) - Using
$assertassertion methods (isTrue, isEqual, includes, throws, etc.) - Writing
beforeTests()/afterTests()/setup()/teardown()lifecycle methods - Unit-testing CFC models, services, or utilities in isolation with mocked dependencies
- Applying the Arrange-Act-Assert (AAA) pattern
Language Reference
| Concept | BoxLang (.bx) preferred | CFML (.cfc) compatible |
|---|---|---|
| Class declaration | class extends="testbox.system.BaseSpec" {} | component extends="testbox.system.BaseSpec" {} |
| Test functions | function testXxx() {} | function testXxx() output="false" {} |
| Scoped var | var x = ... | var x = ... |
Canonical xUnit Bundle Structure
class labels="unit" extends="testbox.system.BaseSpec" {
/****** LIFECYCLE ******/
// Runs ONCE before all test functions in this bundle
function beforeTests() {
variables.service = new models.CalculatorService()
}
// Runs ONCE after all test functions
function afterTests() {
structClear( variables )
}
// Runs before EACH test function
function setup() {
variables.mockLogger = createMock( "models.Logger" )
variables.service.setLogger( mockLogger )
}
// Runs after EACH test function
function teardown() {
mockLogger.$reset()
}
/****** TEST METHODS ******/
function testAddsTwoNumbers() {
// Arrange
var a = 5
var b = 3
// Act
var result = service.add( a, b )
// Assert
$assert.isEqual( 8, result )
}
function testDivideThrowsOnZero() {
$assert.throws(
() => service.divide( 10, 0 ),
"MathException"
)
}
function testSkipped() skip {
$assert.fail( "Should never run" )
}
}
Lifecycle Method Reference
| Method | When It Runs | Use Case |
|---|---|---|
beforeTests() | Once before all test functions | Initialize shared objects, DB connections, JWT settings |
afterTests() | Once after all test functions | Close connections, delete temp files |
setup() | Before each test function | Create fresh mocks, reset state, clear caches |
teardown() | After each test function | Roll back transactions, delete records, reset stubs |
function beforeTests() {
// One-time: load heavy collaborators
variables.orm = getInstance( "ORMService@cborm" )
structClear( request )
}
function setup() {
// Per-test: always get a clean state
variables.mockDAO = createEmptyMock( "models.UserDAO" )
variables.sut = new models.UserService( mockDAO )
}
$assert Assertion Reference
Every test bundle receives $assert — an instance of testbox.system.Assertion.
// Boolean
$assert.isTrue( myBool )
$assert.isFalse( myBool )
// Equality
$assert.isEqual( expected, actual )
$assert.isEqualWithCase( expected, actual )
$assert.isNotEqual( expected, actual )
// Null
$assert.null( actual )
$assert.notNull( actual )
// Emptiness
$assert.isEmpty( target ) // arrays, structs, strings, queries
$assert.isNotEmpty( target )
// Size
$assert.lengthOf( target, length )
$assert.notLengthOf( target, length )
// Key existence
$assert.key( target, key )
$assert.notKey( target, key )
$assert.deepKey( target, key )
$assert.notDeepKey( target, key )
// Inclusion
$assert.includes( target, needle ) // case-insensitive
$assert.includesWithCase( target, needle )
$assert.notIncludes( target, needle )
$assert.notIncludesWithCase( target, needle )
// Type
$assert.typeOf( type, actual )
$assert.notTypeOf( type, actual )
$assert.instanceOf( actual, typeName )
$assert.notInstanceOf( actual, typeName )
// Numeric comparison
$assert.isGT( actual, target )
$assert.isGTE( actual, target )
$assert.isLT( actual, target )
$assert.isLTE( actual, target )
$assert.between( actual, min, max )
$assert.closeTo( expected, actual, delta )
// String / regex
$assert.match( actual, regex )
$assert.matchWithCase( actual, regex )
$assert.notMatch( actual, regex )
// Exceptions
$assert.throws( target, [type], [regex] )
$assert.notThrows( target, [type], [regex] )
// Force failure
$assert.fail( [message] )
// Skip current test
$assert.skip( message, detail )
BoxLang Dynamic Assertion Methods
In BoxLang you can also invoke any assertion as a free function prefixed with assert:
assertIsTrue( myBool )
assertIsEqual( expected, actual )
assertBetween( actual, 1, 100 )
assertThrows( () => badCall(), "MyException" )
Arrange-Act-Assert (AAA) Pattern
function testUserCreation() {
// ARRANGE
var mockUserDAO = createEmptyMock( "models.UserDAO" )
mockUserDAO.$( "save" ).$results( { id: 42, name: "Alice" } )
var sut = new models.UserService( mockUserDAO )
var data = { name: "Alice", email: "[email protected]" }
// ACT
var result = sut.createUser( data )
// ASSERT
$assert.isEqual( 42, result.id )
$assert.isEqual( "Alice", result.name )
$assert.isTrue( mockUserDAO.$once( "save" ) )
}
Mixing xUnit with Expectations (expect DSL)
You can freely mix $assert and expect() fluent matchers in the same bundle:
function testUserEmail() {
var user = sut.findById( 1 )
// xUnit style
$assert.isNotEmpty( user )
$assert.key( user, "email" )
// BDD fluent style (also available in xUnit bundles)
expect( user.email ).toMatch( ".+@.+" )
expect( user.isActive ).toBeTrue()
}
Skipping Tests
// Skip via function attribute
function testSomething() skip {
$assert.fail( "won't run" )
}
// Skip via argument
function testEngineSpecific() skip="#!server.keyExists( 'lucee' )#" {
$assert.isTrue( luceeOnlyFeature() )
}
// Skip programmatically inline
function testConditional() {
if ( !featureEnabled ) {
$assert.skip( "Feature flag is off" )
}
$assert.isTrue( myFeature.isActive() )
}
Custom Assertions
Register in beforeTests() to keep the shared $assert object clean:
function beforeTests() {
addAssertions( {
isValidEmail: function( actual ) {
return ( reFindNoCase( "^[^@]+@[^@]+\.[^@]+$", actual ) > 0
? true
: fail( "[#actual#] is not a valid email address" ) )
},
isUUID: function( actual ) {
return ( isValid( "uuid", actual )
? true
: fail( "[#actual#] is not a UUID" ) )
}
} )
}
function testEmailValidator() {
$assert.isValidEmail( "[email protected]" )
$assert.isValidEmail( "not-an-email" ) // will fail
}
For reusable assertion libraries, register a class path or instance:
function beforeTests() {
addAssertions( "tests.helpers.CustomAssertions" )
// or
addAssertions( new tests.helpers.CustomAssertions() )
}
Key Differences: xUnit vs BDD
| Aspect | xUnit | BDD |
|---|---|---|
| Test declaration | function testXxx() | it( "...", () => {} ) |
| Suite declaration | Class-level | describe( "...", () => {} ) |
| Lifecycle | beforeTests/setup/teardown/afterTests | beforeAll/beforeEach/afterEach/afterAll/aroundEach |
| Assertions | $assert.isXxx() | expect().toBeXxx() |
| Skip | skip function attribute | xit(), skip() inline |
| Nesting | Not supported | Unlimited nested describe blocks |
| Data binding | Not supported | it( data={} ) |
CommandBox Scaffolding
# Create xUnit spec
coldbox create unit name=UserServiceTest open=true
# Scaffold with specific model binding
coldbox create unit name=UserServiceTest methods=testCreate,testUpdate,testDelete
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most test skills give in ~2.0k tokens
Counted across 964 of the 1,571 authors here whose files we hold, read 2026-08-07
- Close the browser when donein 55 of 964, across 12 files
- Wait for network idle statein 51 of 964, across 6 files
- Launch Chromium in headless modein 49 of 964, across 6 files
- Use descriptive selectors for elementsin 49 of 964, across 6 files
- Run provided scripts with help flag firstin 49 of 964, across 6 files
- Add appropriate explicit waitsin 48 of 964, across 5 files
- Use bundled scripts as black boxesin 46 of 964, across 3 files
- Do not read script source codein 46 of 964, across 3 files
- Use sync playwright for scriptsin 46 of 964, across 3 files
- Inspect dom before executing actionsin 46 of 964, across 3 files
- Run the full test suitein 37 of 964
- Write the failing test firstin 29 of 964, across 23 files
Said here and by no other author read
- extend the testbox system BaseSpec class
- use beforeTests to initialize shared objects
- use setup to create fresh mocks before each test
- use teardown to reset stubs after each test
- use afterTests to clean up shared resources
- skip tests using the skip function attribute
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.