Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
TestBox 2.5.0 is a minor release with some great new functionality and tons of fixes. You can find the release notes here and the major updates for this release. One of the biggest features for TestBox that was not part of TestBox, was the addition of TestBox Watchers to CommandBox.
CommandBox CLI has added watching capabilities to the testbox
namespace. Which means you can now run the following command in the root of your project: testbox watch
and it will monitor all your CFCs for changes. If a change is detected, then it will fire the new testbox run
command with our CLI reporter.
We had introduced spec data binding in TestBox for creating dynamic specs and passing dynamic data into them. We have extended this feature into life-cycle methods:
beforeEach()
afterEach()
aroundEach()
You can now pass in an argument called data
which is a struct of dynamic data to pass into the life-cycle method. You can then pickup this data in the closure for the life-cycle. Here is a typical example:
[TESTBOX-177] - Simple reporter broken on Adobe servers
[TESTBOX-180] - HTML Runner links should include directory param
[TESTBOX-181] - AsyncAll Errroring on xUnit runner
[TESTBOX-183] - the 'type' argument was being ignored if the 'regex' argument was provided and matched the exception data
[TESTBOX-178] - Remove thread scope from debug stream
[TESTBOX-184] - Passing Data to Dynamic Tests via life-cycle methods: around, after, before
[TESTBOX-185] - Preserve whitespace in HTML (simple) Reporter
[TESTBOX-186] - Include TestBox version in TestResult memento
[TESTBOX-187] - Let travis handle upload of api docs and snapshot binaries
[TESTBOX-188] - Aggregate suite stats from nested suties
TestBox 2.2.0 is a minor release with some great new functionality and tons of fixes.
Bugs
[TESTBOX-48] - Syntax error in docs for Assertion Registration
[TESTBOX-49] - Docs for Assertion CFCs inadequate, or functionality just doesn't work?
[TESTBOX-120] - MXUnit injectMethod not working if calls to methods being replaced are not "this" scoped
[TESTBOX-122] - Order of args for init function did not match the interface
[TESTBOX-134] - Mockbox: object instances as args do not always pass comparison
New Features
[TESTBOX-129] - throwErrorCode addition to mock method generator
[TESTBOX-132] - Ability to bind data to a spec
[TESTBOX-133] - Support story-given-when-then style of representing tests
[TESTBOX-136] - MockBox no longer standalone package
[TESTBOX-138] - run method listeners so you can do callbacks on testing progress
Improvements
[TESTBOX-121] - Ability to read assets from current directory to support lucee
[TESTBOX-124] - Support Lucee's localMode="modern"
[TESTBOX-126] - it() callback should not need to be a closure
[TESTBOX-130] - Mocking method CallBack argument receives all arguments now.
[TESTBOX-135] - Change toIncludeWithCase arguments to match toInclude
TestBox 2.8.0 is a minor release with some great new functionality and tons of fixes. You can find the release notes here and the major updates for this release.
[TESTBOX-224] - Bug on SimpleReporter name not complete
[TESTBOX-231] - Recurse parameter not honored #71 on html runner
[TESTBOX-232] - Junit reporter blows up on null values in server scope
[TESTBOX-233] - Update Expectation.cfc
to make toMatchWithCase
consistent as it is not working
[TESTBOX-225] - Add ability to exclude labels from running using `url.excludes`
[TESTBOX-230] - Incompatibilities with Adobe on many isValid
and isInstanceOf
TestBox 2.7.0 is a minor release with some great new functionality and tons of fixes. You can find the release notes here and the major updates for this release.
[TESTBOX-217] - new version of Lucee has complex values in UDF implementation that breaks the mock generator
[TESTBOX-222] - Heap issues and stack overflow issues when normalizing ORM entities in mocking arguments
[TESTBOX-221] - Complete refactoring of around,before,after each executions to support concurrency
[TESTBOX-219] - some refactoring to use invoke()
instead of evaluate()
[TESTBOX-220] - some speed improvements by not using createuuid
anymore
TestBox is a next generation testing framework for ColdFusion (CFML) that is based on BDD (Behavior Driven Development) for providing a clean obvious syntax for writing tests. It contains not only a testing framework, runner, assertions, mocking/stubbing and expectations library. It also supports xUnit style of testing with MXUnit compatibilities.
Important TestBox is a standalone testing package and is meant to be used with any ColdFusion (CFML) application, framework or library. IT IS NOT ONLY FOR COLDBOX APPLICATIONS.
If you are going to be using the xUnit style of testing which includes MXUnit compatibility, then you don't need the latest CFML engines. BDD uses closures to declare your specs, so it it is only available on newer versions of your CFML engine.
ColdFusion 10+
Lucee 4.5+
TestBox is a xUnit style and behavior-driven development (BDD) testing framework for CFML (ColdFusion). It does not depend on any other framework for operation and it has a clean and descriptive syntax that will make you giggle when writing your tests. It ships with not only the xUnit and BDD testing capabilities, but also an assertions and expectations library, several different runners, reporters and MockBox, for mocking and stubbing capabilities.
TestBox is a next generation testing framework for ColdFusion (CFML) that is based on BDD (Behavior Driven Development) for providing a clean obvious syntax for writing tests. It contains not only a testing framework, runner, assertions and expectations library but also ships with MockBox, A mocking and stubbing companion. It also supports xUnit style of testing and MXUnit compatibilities.
Here are a simple listing of features TestBox brings to the table:
BDD style testing
xUnit style testing
Testing life-cycle methods
MockBox integration for mocking and stubbing
Ability to extend and create custom test runners
Ability to extend and create custom test reporters
Extensible reporters, bundled with tons of them:
JSON
XML
JUnit 4 XML
Text (80's style)
Console
TAP (Test Anything Protocol)
Simple HTML
Min - Minimalistic Heaven
Raw
CommandBox
Asynchronous testing
Multi-suite capabilities
Test skipping
Suite skipping
Dynamic skipping support via runtime executions
Test one or more suites exclusively
Test one or more tests/specs exclusively
Test labels and tagging
Testing debug output stream
Clickable suite titles to filter test execution
Much more!
TestBox is maintained under the Semantic Versioning guidelines as much as possible.Releases will be numbered with the following format:
And constructed with the following guidelines:
Breaking backward compatibility bumps the major (and resets the minor and patch)
New additions without breaking backward compatibility bumps the minor (and resets the patch)
Bug fixes and misc changes bumps the patch
TestBox and MockBox are open source and licensed under the Apache 2 License. If you use them please try to make mention of it in your code or web site.
Copyright by Ortus Solutions, Corp
TestBox is a registered trademark by Ortus Solutions, Corp
Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.
BoxTeam Slack : https://boxteam.herokuapp.com
CFML Slack: Look for the box-products
channel: http://cfml.slack.com
We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us: https://github.com/Ortus-Solutions/TestBox
TestBox is a professional open source software backed by Ortus Solutions, Corp offering services like:
Custom Development
Professional Support & Mentoring
Training
Server Tuning
Security Hardening
Code Reviews
Official Site: https://www.ortussolutions.com/products/testbox
Current API Docs: http://apidocs.ortussolutions.com/testbox/current
Source Code: https://github.com/Ortus-Solutions/TestBox
Bug Tracker : https://ortussolutions.atlassian.net/browse/TESTBOX
Twitter: @ortussolutions
Facebook: https://www.facebook.com/ortussolutions
Because of His grace, this project exists. If you don't like this, then don't read it, its not for you.
Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God. - Romans 5:5
TestBox has the following supported IDE Tools:
Sublime -
VSCode -
CFBuilder -
The source code for this book is hosted in GitHub: . You can freely contribute to it and submit pull requests. The contents of this book is copyright by and cannot be altered or reproduced without author's consent. All content is provided "As-Is" and can be freely distributed.
The majority of code examples in this book are done in cfscript
.
The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL -
All ColdFusion examples designed to run on the open soure Railo Platform or Adobe ColdFusion 9.0.2+
Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.
The information in this book is distributed “as is”, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity with respect to loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software and resources described in it.
We highly encourage contribution to this book and our open source software. The source code for this book can be found in our where you can submit pull requests.
15% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - . So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.
Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education and life skills training in a Christian environment. The home is supported by a child sponsorship program.
We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador that either have no families or have been abandoned. This is good earth to seed and plant.
TestBox is a next generation testing framework for ColdFusion (CFML) that is based on (Behavior Driven Development) for providing a clean obvious syntax for writing tests. It contains not only a testing framework, runner, assertions and expectations library but also ships with MockBox, A mocking and stubbing companion. It also supports xUnit style of testing and MXUnit compatibilities.
Here are a simple listing of features TestBox brings to the table:
BDD style testing
xUnit style testing
Testing life-cycle methods
Ability to extend and create custom test runners
Ability to extend and create custom test reporters
Extensible reporters, bundled with tons of them:
JSON
XML
JUnit 4 XML
Text (80's style)
Console
Simple HTML
Min - Minimalistic Heaven
Raw
CommandBox
Asynchronous testing
Multi-suite capabilities
Test skipping
Suite skipping
Dynamic skipping support via runtime executions
Test one or more suites exclusively
Test one or more tests/specs exclusively
Test labels and tagging
Testing debug output stream
Clickable suite titles to filter test execution
Much more!
And constructed with the following guidelines:
Breaking backward compatibility bumps the major (and resets the minor and patch)
New additions without breaking backward compatibility bumps the minor (and resets the patch)
Bug fixes and misc changes bumps the patch
Copyright by Ortus Solutions, Corp
TestBox is a registered trademark by Ortus Solutions, Corp
Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.
Custom Development
Professional Support & Mentoring
Training
Server Tuning
Security Hardening
Code Reviews
Because of His grace, this project exists. If you don't like this, then don't read it, its not for you.
Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God. - Romans 5:5
TestBox 2.4.0 is a minor release with some great new functionality and tons of fixes. This release has been a great community effort as many people in the community contributed to its release. Special thanks to Eric Peterson, Joe Gooch and Sean Corfield for their additions, testing and contributions.
toSatisfy
matcherThis new matcher is thanks to Sean Corfield. It allows you to create your own closure that will evaluate the expectation and then decide if it passes the given truth test.
expectAll()
collection expectationSean was busy in this release and provided us with this awesome feature in which you can call on a new expectAll()
and pass either an array or struct. TestBox will then iterate for you and call all the chained matchers upon the collection items.
mintext
ReporterThis new reporter is to enhance console based runners in order for the report to be more legible.
You can now use a toBeJSON()
matcher or a $assert.isJSON
assertion.
runRemote
You no longer need to pass ?method=runRemote
in the URL when executing a test bundle via the URL. This will automatically be added for you.
Thanks to Joe Gooch you can now use the new methods in the TestBox cfc
addDirectory()
addDirectories()
addBundles()
You can chain them as you see fit and they will aggregate the specs collected.
Here is the full release notes for this release
TestBox 2.6.0 is a minor release with some great new functionality and tons of fixes. You can find the release notes here and the major updates for this release.
The methods aroundEach(), beforeEach() and afterEach()
can now be chained as they return the Base Spec as well. So get funky with the DSL.
You can now pass null
into an expectation and TestBox will gracefully represent it instead of blowing up!
Thanks to method mocking performance has sky rocketed. He changed the filename generation to an MD5 hash of the code, and disabled removing the files. This allows TestBox to leverage ColdFusion template caching. This reduces the time that $()
takes from ~30 ms to ~0 ms. It is a happy day for mocking!
We have added a new argument to all TestBox run(), runRemote(), runRaw()
commands: eagerFailure
, which defaults to false
. If this is turned on, then TestBox will gracefully short-circuit out of testing further Test Bundles if it finds any failures or errors on previous ones. This is useful for large suites that you want to stop testing if a failure is discovered.
This was another community driven contribution by to help those folks with . You can see the differences in color in the github pull request:
Before
After
x
exclusion MethodsYou can now prefix the following methods with the letter x
to exclude them from execution:
story()
given()
when()
then()
feature()
TestBox can be downloaded from or can be installed via . CommandBox is our preferred approach for all package installations, updates and more.
Note Please note the
--saveDev
flag which tells CommandBox that TestBox is a development dependency and not a production dependency.
This will install TestBox in a /testbox folder from where you called the command. You can also extract the download zip and place it anywhere you like and create a mapping called /testbox that points to testbox in the distribution folder, this is the most secure approach. However, you can just place it in the webroot if you like.
You can also clone from and help us out :)
Shalom Children’s Home () is one of the ministries that is dear to our hearts located in El Salvador. During the 12 year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children werecame on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.
integration for mocking and stubbing
TAP ()
TestBox is maintained under the guidelines as much as possible.Releases will be numbered with the following format:
TestBox and MockBox are open source and licensed under the License. If you use them please try to make mention of it in your code or web site.
Help Group :
BoxTeam Slack :
CFML Slack: Look for the box-products
channel:
We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us:
By Jira:
TestBox is a professional open source software backed by offering services like:
Official Site:
Current API Docs:
Help Group:
Source Code:
Bug Tracker :
Twitter:
Facebook:
[] - discover if fail origin exists in errors and failures, else ignore as it causes issues
[] - Custom reporter passed as CFC instance doesn't work
[] - New mintext reporter
[] - new matcher toBeJSON and new assertion isJSON
[] - No need to pass method=runRemote anymore on spec runners, defaults now
[] - Implements fluent API - addDirectory,addBundles,addDirectories on TestBox Core
[] - runRemote operations are not setting the default response to HTML, so wddx takes over
[] - Add toSatisfy( predicate ) matcher
[] - Add expectAll() to make it easier to work with collections
[] - Assertion argument order does not match testbox.system.assertion for the expected argument
[] - MockBox method subs are leaking whitespace
[] - Labels in an it() are ignored
[] - The recurse checkbox in test-runner doesn't work
[] - TestBox doesn't handle an interface CFC in the specs folder and chokes
[] - Return this from around,beforeEach and afterEach to have a chained DSL thanks to Jose Chavez
[] - Output null representation in output message
[] - wrap bundle runner execution in try/catch with rethrow
[] - Improved method mocking performance
[] - Provide a way to mute debug buffer on marshalled results
[] - Add eager failure argument to runner so if set and a bundle fails no more bundles are tested after
[] - Use correct key casing in report output
[] - switch pass/failed/error colors to help color blindness
[] - Exclude empty test suites from ANT JUnit Reporting
[] - Add x- methods to skip suites and specs to new BDD methods.
[] - Don't re-mock already mocked objects. #63
TestBox relies on the fact of creating testing bundles which are basically CFCs. A bundle CFC will hold all the suites and specs a TestBox runner will execute and produce reports on.
This bundle CFC can contain 2 life-cycle functions and a single run()
function where you will be writing your test suites and specs. The beforeAll()
and afterAll()
methods are called life-cycle methods. They will execute once before the run()
function and once after the run()
function. This is a great way to do any kind of global setup or teardown in your tests.
The run()
function receives the TestBox testResults
object as a reference and testbox
as a reference as well. This way you can have metadata and access to what will be reported to users in a reporter. You can also use it to decorate the results or store much more information that can be picked up later by reports. You also have access to the testbox
class so you can see how the test is supposed to execute, what labels was it passed, directories, options, etc.
TestBox does not depend on any other framework for operation and it has a clean and descriptive syntax that will make you giggle when writing your tests. It ships with not only the xUnit and BDD testing capabilities, but also an assertions and expectations library, several different runners, reporters and MockBox, for mocking and stubbing capabilities.
The download structure includes:
apidocs : The API docs for TestBox
system : The main system framework folder
test-browser : This is a little utility to facilitate navigating big testing suites. This helps navigate to the suites you want and execute them instead of typing all the time.
test-harness : A simplified version of the TestBox runner that can be placed anywhere in your application. It includes an ANT build that allows you to execute your tests and produce results via ANT and also JUnit compliant reports via the junitreport task.
test-runner : The pre-built TestBox Global Runner. You can pass in a directory mapping or bundles and select labels and boom run the tests.
tests : Several sample tests and runners which actually are used to build TestBox
The only required folder is system and it does not need to be web-accessible. The other folders are all optional tools to help you that expect to be web-accessible. You can safely remove everything except the system folder if you want to build your own test browsers. If you are placing TestBox outside of your web root, the /testbox
mapping needs to point to the parent folder containing the system
directory as all TestBox models start with testbox.system
in their package path.
A test suite begins with a call to our TestBox describe()
function with at least two arguments: a title
and a body
function/closure. The title
is the name of the suite to register and the body
function/closure is the block of code that implements the suite. When applying BDD to your tests, this function is used to describe your story scenarios that you will implement. There are more arguments which you can see below:
Luis Majano is a Computer Engineer with over 16 years of software development and systems architecture experience. He was born in in the late 70’s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelors of Science in Computer Engineering at . Luis resides in The Woodlands, Texas with his beautiful wife Veronica, baby girl Alexia and baby boy Lucas!
Luis has a passion for Jesus, tennis, golf, volleyball and anything electronic. Random Author Facts:
He played volleyball in the Salvadorean National Team at the tender age of 17
The Lord of the Rings and The Hobbit is something he reads every 5 years. (Geek!)
His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)
He has a geek love for circuits, microcontrollers and overall embedded systems.
He has of late (during old age) become a fan of running and bike riding with his family.
Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!
“Trust in the LORD with all your heart, and do not lean on your own understanding.” Proverbs 3:5
Jorge started working as project manager and business developer at Ortus Solutions, Corp. in 2013, . At Ortus he fell in love with software development and now enjoys taking part on software development projects and software documentation! He is a fellow Cristian who loves to play the guitar, worship and rejoice in the Lord!
Therefore, if anyone is in Christ, the new creation has come: The old has gone, the new is here! 2 Corinthians 5:17
With TestBox's BDD syntax, it is possible to create suites dynamically; however, there are a few things to be aware of.
Setup for dynamic suites must be done in the pseudo-constructor (versus in beforeAll()
). This is because variables
-scoped variables set in beforeAll()
are not available in the describe
closure (even though they are available in it
closures). This behavior can be explained by the execution sequence of a BDD bundle: When the bundle's run()
method is called, it collects preliminary test data via describe
s. After preliminary test data are collected, the beforeAll()
runs, followed by the describe
closures.
Additionally, care must be taken to pass data into the it
closures, otherwise strange behavior will result (the values from the last loop iteration will be repeated in the body of each looped it
).
The following bundle creates suites dynamically, by looping over test metadata.
TestBox 2.1.0 is a minor release with some great new functionality and tons of fixes.
Bugs Fixed
[] - isEqual on Query fails when queries are equal
[] - equalize fails on struct/objects/arrays when null values exist within them
[] - Floating Point Number isEqual Fails
[] - Specs with the same name cause thread name exceptions when using async
[] - Download file has "samples" instead of "tests" directory
[] - tobe() cannot handle sparse arrays on Adobe CF
[] - xUnit compatibility CF9 broken due to isClosure() being utilized
[] - skip closures get more metadata arguments when being executed.
[] - testbox errors when using complete null support in railo
[] - Have debug() include information about where it came from
[] - remove extra whitespace in text reporter
[] - Remove CF7,8 incompatibilities
[] - ColdFusion 11 cfinclude compatibilities
Better whitespace management on text enabled reporting
ColdFusion 11 compatibilities
ColdFusion 9 support on xUnit and Assertions
HTML Runners now have a high request timeout for long-lasting tests
TestBox's debug()
method has been enhanced to provide greater messages and telemetry. You can even control the depth of the dumps of each of the debugged data as it is sent. Here is the new signature:
The output will now include information as to what spec produced the output, timestamps, data, and thread data.
You can now chain your matchers on a specific expectation to produce an even nicer DSL when trying to assert data on a single expectation:
AroundEach()
Life-Cycle MethodWe have added a new life-cycle method to the suites called aroundEach()
which will completely wrap your spec in another closure. This is an elegant way for you to provide a complete around AOP advice to a specificiation. You can use it to surround the execution in transaction blocks, ORM rollbacks, logging, and so much more. This life-cycle method will decorate ALL specificiations within a single suite. The method signature is below:
The method receives a structure of data representing the spec
. This contains the following elements:
body : The actual closure for the spec that you will use to execute within it.
labels : The labels used in the spec
name : The name of the spec
order : The order of execution of the spec
skip : The skip flag or closure that determines if the spec runs
The method also receives a structure of metadata about the suite this spec is contained in. It has information about life-cycle closures, async information, names, parents, etc. Here is a very practical example of creating and around each closure to provide rollbacks for specs:
This simple around each life-cycle closure will rollback ALL my spec's executions even if they throw exceptions.
The simple reporter has been enhanced to add a bundle test filter that can help reduce noise when looking for a specific bundle result:
run()
EnhancementsThe traditional BDD run()
has been enhanced so it now receives the TestBox TestResults object as a reference and TestBox as a reference. This way you can have more metadata and access to what will be reported to users in a reporter. You can also use it to decorate it or store much more information that can be picked up later by reports. You also have access to the TestBox class so you can see how the test is supposed to execute, what labels was it passed, directories, options, etc.
Expectations are self-concatenated strings that evaluate an actual value to an expected value or condition. These are initiated by the global TestBox method called expect()
which takes in a value called the actual value or expectAll()
which takes in an array or struct which will be the actual value. It is concatenated in our expectations DSL with a matcher function that will most likely take in an expected value or condition to test. You can also concatenate the matchers and do multiple evaluations on a single actual value.
Each matcher implements a comparison or evaluation of the actual value and an expected value or condition. It is responsible for either passing or failing this evaluation and reporting it to TestBox. Each matcher also has a negative counterpart assertion by just prefixing the call to the matcher with a not
expression.
Specifications (Specs) are defined by calling the TestBox it()
global function, which takes in a title
and a body
function/closure. The title
is the title of this spec you will write and the body
function/closure is a block of code that represents the spec. A spec will contain most likely one or more expectations that will test the state of the (software under test) or sometimes referred to as code under test. In BDD style, your specifications are what is used to validate your requirements of a scenario which is your describe()
block of your .
An expectation is a nice assertion DSL that TestBox exposes so you can pretty much read what should happen in the testing scenario. A spec will pass if all expectations pass. A spec with one or more expectations that fail will fail the entire spec.
The data
argument can be used to pass in a structure of data into the spec so it can be used later within the body closure. This is great when doing looping and creating dynamic closure calls:
Specs and suites can be tagged with TestBox labels. Labels allows you to further categorize different specs or suites so that when a runner executes with labels attached, only those specs and suites will be executed, the rest will be skipped.
Please refer to our section to take advantage of all the mocking and stubbing you can do. However, every BDD TestBundle has the following functions available to you for mocking and stubbing purposes:
makePublic( target, method, newName )
- Exposes private methods from objects as public methods
querySim( queryData )
- Simulate a query
getMockBox( [generationPath] )
- Get a reference to MockBox
createEmptyMock( [className], [object], [callLogging=true])
- Create an empty mock from a class or object
createMock( [className], [object], [clearMethods=false], [callLogging=true])
- Create a spy from an instance or class with call logging
prepareMock( object, [callLogging=true])
- Prepare an instance of an object for method spies with call logging
createStub( [callLogging=true], [extends], [implements])
- Create stub objects with call logging and optional inheritance trees and implementation methods
getProperty( target, name, [scope=variables], [defaultValue] )
- Get a property from an object in any scope
As you can see from our arguments for a test suite, you can pass an asyncAll
argument to the describe()
blocks that will allow TestBox to execute all specs in separate threads for you concurrently.
Caution Once you delve into the asynchronous world you will have to make sure your tests are also thread safe (var-scoped) and provide any necessary locking.
is a style of writing tests where you describe the state of the code you want to test (Given
), the behavior you want to test (When
) and the expected outcome (Then
). (See )
Testbox supports the use of function names given()
and when()
in-place of describe()
function calls. The then()
function call is an alternative for it()
function calls. The advantage of this style of is that you can gather your requirements and write your tests in a common language that can be understood by developers and stake-holders alike. This common language format is often referred to as the language; using it we can gather and document the requirements as:
TestBox provides you with feature()
, scenario()
and story()
wrappers for describe()
blocks. As such we can write our requirements in test form like so:
The output from running the test will read as the original requirements, providing you with not only automated tests but also a living document of the requirements in a business-readable format.
As feature()
, scenario()
and story()
are wrappers for describe()
you can intermix them so that your can create tests which read as the business requirements. As with describe()
, they can be nested to build up blocks.
TestBox ships with several test runners internally but we have tried to simplify and abstract it with our testbox
object which can be found in the testbox.system
package. The TestBox object allows you to execute tests from a variety of entry points and languages such as CFC, CFM, HTTP, NodeJS, SOAP or REST. You can also make your CFC's extend from our BaseSpec
class so you can execute it directly via the URL. The main execution methods are:
Info We encourage you to read the API docs included in the distribution for the complete parameters for each method.
run()
ArgumentsHere are the arguments you can use for initializing TestBox or executing the run()
method
runRemote()
argumentsHere are the arguments you can use for executing the runRemote()
method:
The bundles argument which can be a single CFC path or an array of CFC paths or a directory argument so it can go and discover the test bundles from that directory. The reporter argument can be a core reporter name like: json,xml,junit,raw,simple,dots,tap,min,etc or it can be an instance of a reporter CFC. You can execute the runners from any cfm template or any CFC or any URL, that is up to you.
TestBox ships with a global runner that can be used to run pretty much anything. You can customize it or place it wherever you need it:
TestBox ships with a test browser that is highly configurable to whatever URL accessible path you want. It will then show you a test browser where you can navigate and execute not only individual tests, but also directory suites as well.
In our test samples and templates we include an ANT runner that will be able to execute your tests via ANT. It can also leverage our ANTJunit reporter to use the junitreport task to produce JUnit compliant reports as well. You can find this runner in the test samples and runner template directory.
You can run tests via SOAP by leveraging the runRemote() method. The WSDL URL will be
HTTP/REST Runner You can run tests via HTTP/REST by leveraging the runRemote() endpoint. The URL will be
There is a user-contributed NodeJS Runner that looks fantastic and can be downloaded here:
Just use node to install:
He is the CEO of , a consulting firm specializing in web development, ColdFusion (CFML), Java development and all open source professional services under the ColdBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, MockBox, LogBox and anything “BOX”, and contributes to many open source ColdFusion projects. He is also the Adobe ColdFusion user group manager for the . You can read his blog at
Jorge is an Industrial and Systems Engineer born in El Salvador. After finishing his Bachelor studies at the Monterrey Institute of Technology and Higher Education , Mexico, he went back to his home country where he worked as the COO of. In 2012 he left El Salvador and moved to Switzerland in persuit of the love of his life. He married her and today he resides in Basel with his lovely wife Marta and their daughter Sofía.
[] - BDD run() method now receive the TestResults argument for usage in their definitions.
[] - BDD runner and specs receive reference to the TestBox calling class via the run() method
[] - Update the apidocs with our new DocBox skin
[] - Debug labels and telemetry additions
[] - Add "top" attribute to debug method
[] - HTMLRunner add big request timeout setting to avoid server cut offs
[] - have expectations assertions return the expectation to allow chaining
[] - Simple reporter includes now a test bundle filter
[] - New lifecycle method: aroundEach() so you can do a full AOP advice on any spec
New searchable API Docs with new skin:
TestBox has a plethora (That's Right! I said ) of matchers that are included in TestBox. The best way to see all the latest matchers is to visit our and digest the testbox.system.Expectation
class. There is also the ability to register and write custom matchers in TestBox via our addMatchers()
function at runtime.
You can also build and register custom matchers. Please visit the Custom Matchers chapter to read more about .
Since the implementations of the describe()
and it()
functions are closures, they can contain executable code that is necessary to implement the test. All CFML rules of scoping apply to , so please remember them. We recommend always using the variables
scope for easy access and distinction.
If you prefer to gather requirements as then you may prefer to take advantage of the story()
wrapper for describe()
instead.
If you make your test bundle CFC inherit from our testbox.system.BaseSpec class, you will be able to execute the CFC directly via the URL: ```javascript ``` You can also pass the following arguments to the method via the URL: * **testSuites** : A list or array of suite names that are the ones that will be executed ONLY! * **testSpecs** : A list or array of test names that are the ones that will be executed ONLY! * **reporter** : The type of reporter to run the test with ```javascript ```
Argument | Required | Default | Type | Description |
bundles | true | --- | string/array | The path, list of paths or array of paths of the spec bundle CFCs to run and test |
directory | false | --- | struct | The directory mapping path or a struct: [ mapping = the path to the directory using dot notation (myapp.testing.specs), recurse = boolean, filter = closure that receives the path of the CFC found, it must return true to process or false to continue process ] |
reporter | false | simple | string/struct/instance | The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or an instance of a coldbox.system.reports.IReporter. You can also pass a struct with [type="string or classpath", options={}] if a reporter expects options. |
labels | false | false | string/array | The string or array of labels the runner will use to execute suites and specs with. |
excludes | false | --- | string/array | The string or array of labels to exclude from running |
options | false | {} | struct | A structure of property name-value pairs that each runner can implement and use at its discretion. |
testBundles | false | --- | string/array | A list or array of bundle names that are the ones that will be executed ONLY! |
testSuites | false | --- | string/array | A list or array of suite names that are the ones that will be executed ONLY! |
testSpecs | false | --- | string/array | A list or array of test names that are the ones that will be executed ONLY |
callbacks | false |
| struct of closures or CFC | A struct of listener callbacks or a CFC with callbacks for listening to progress of the testing: |
eagerFailure | false | false | boolean | If true, then after testing a bundle if there are any failures or errors no more testing will be performed. |
Argument | Required | Default | Type | Description |
bundles | true | --- | string | The path, list of paths or array of paths of the spec bundle CFCs to run and test |
directory | false | --- | string | The directory mapping to test: directory = the path to the directory using dot notation (myapp.testing.specs) |
recurse | false | true | boolean | Recurse the directory mapping or not, by default it does |
reporter | false | simple | string/path | The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or a class path to the reporter to use. |
reporterOptions | false | {} | JSON | A JSON struct literal of options to pass into the reporter |
labels | false | false | string | The string array of labels the runner will use to execute suites and specs with. |
excludes | false | --- | string/array | The string or array of labels to exclude from running |
options | false | {} | JSON | A JSON struct literal of configuration options that are optionally used to configure a runner. |
testBundles | false | --- | string/array | A list or array of bundle names that are the ones that will be executed ONLY! |
testSuites | false | --- | string | A list of suite names that are the ones that will be executed ONLY! |
testSpecs | false | --- | string | A list of test names that are the ones that will be executed ONLY |
eagerFailure | false | false | boolean | If true, then after testing a bundle if there are any failures or errors no more testing will be performed. |
Argument | Required | Default | Type | Description |
title | true | --- | string | The title of the suite to register |
body | true | --- | closure/udf | The closure that represents the test suite |
labels | false | --- | string/array | The list or array of labels this suite group belongs to |
asyncAll | false | false | Boolean | If you want to parallelize the execution of the defined specs in this suite group. |
skip | false | false | Boolean | A flag or a closure that tells TestBox to skip this suite group from testing if true. If this is a closure it must return boolean. |
Argument | Required | Default | Type | Description |
title | true | --- | string | the title of the spec |
body | true | --- | closure/udf | The closure that represents the spec |
labels | false | --- | string/array | The list or array of labels this suite group belongs to |
skip | false | false | Boolean | A flag or a closure that tells TestBox to skip this suite group from testing if true. If this is a closure it must return boolean. |
data | false |
| struct | A struct of data you can bind the spec with so you can use within the |
TestBox is a xUnit style and behavior-driven development (BDD) testing framework for CFML (ColdFusion). It does not depend on any other framework for operation and it has a clean and descriptive syntax that will make you giggle when writing your tests. It ships with not only the xUnit and BDD testing capabilities, but also an assertions and expectations library, several different runners, reporters and MockBox, for mocking and stubbing capabilities.
Info Note: This article is a primer to get you started with the xUnit capabilities of TestBox, if you would like to explore the BDD style capabilities of TestBox we recommend you visit the BDD primer chapter.
As we have seen before, the describe()
function describes a test suite of related specs in a test bundle CFC. The title of the suite is concatenated with the title of a spec to create a full spec's name which is very descriptive. If you name them well, they will read out as full sentences as defined by BDD style.
Calls to our describe()
function can be nested with specs at any level or point of execution. This allows you to create your tests as a related tree of nested functions. Please note that before a spec is executed, TestBox walks down the tree executing each beforeEach()
and afterEach()
function in the declared order. This is a great way to logically group specs in any level as you see fit.
TestBox comes also with a nice plethora of reporters:
ANTJunit : A specific variant of JUnit XML that works with the ANT junitreport task
Codexwiki : Produces MediaWiki syntax for usage in Codex Wiki
Console : Sends report to console
Doc : Builds semantic HTML to produce nice documentation
Dot : Builds an awesome dot report
JSON : Builds a report into JSON
JUnit : Builds a JUnit compliant report
Raw : Returns the raw structure representation of the testing results
Simple : A basic HTML reporter
Text : Back to the 80's with an awesome text report
XML : Builds yet another XML testing report
Tap : A test anything protocol reporter
Min : A minimalistic view of your test reports
MinText : A minimalistic view of your test reports in consoles
NodeJS : User-contributed: https://www.npmjs.com/package/testbox-runner
The xUnit functionalities of TestBox will require Railo 4.1+, Lucee 4.5+ or ColdFusion 9.01+.
TestBox relies on the fact of creating testing bundles which are basically CFCs. A bundle CFC will hold all the tests the TestBox runner will execute and produce reports on. Thus, sometimes this test bundle is referred to as a test suite in xUnit terms.
This bundle can contain 2 life-cycle methods, the beforeTests()
and afterTests()
methods will execute once before ALL your tests run and then after ALL your tests run. This is a great way to do any kind of global setup or teardown in your tests. It also contains a displayName
argument in the component declaration which gives you a way to name your testing suite. We will see later the other annotations you can add to the component declaration further in the primer.
Assertions are self-concatenated strings that evaluate an actual value to an expected value or condition. These are initiated by the global TestBox variable called $assert which contains tons of included assertion methods so you can evaluate your tests.
Each assertion evaluator will compare the actual value and an expected value or condition. It is responsible for either passing or failing this evaluation and reporting it to TestBox. Each evaluator also has a negative counterpart assertion by just prefixing the call to the method with a not expression.
TestBox has a plethora (That's Right! I said Plethora) of evaluators that are included in the release. The best way to see all the latest evaluator methods is to visit our API and digest the coldbox.system.Assertion class. There is also the ability to register and write custom assertion evaluators in TestBox via our addAssertions() function.
You can also register custom assertions within the $assert object. You will do this by reading our Custom Assertions section of our TestBox docs.
If you are familiar with xUnit style frameworks, the majority of them provide a way to execute functions before and after every single test case or spec in BDD. This is a great way to keep your tests DRY (Do not repeat yourself)! TestBox provides the beforeEach()
and afterEach()
methods that each take in a closure as their argument that receive the name of the spec that's about to be executed or just executed. As their names indicate, they execute before a spec and after a spec in a related describe
block.
The aroundEach()
life-cycle method will completely wrap your spec in another closure. This is an elegant way for you to provide a complete around AOP advice to a specification. You can use it to surround the execution in transaction blocks, ORM rollbacks, logging, and so much more. This life-cycle method will decorate ALL specs within a single suite and any children suites. The method signature is below:
The method receives a structure of data representing the spec. This contains the following elements:
body : The actual closure for the spec that you will use to execute within it.
labels : The labels used in the spec
name : The name of the spec
order : The order of execution of the spec
skip : The skip flag or closure that determines if the spec runs
The method also receives a structure of metadata about the suite this spec is contained in. It has information about life-cycle closures, async information, names, parents, etc. Here is a very practical example of creating and around each closure to provide rollbacks for specs:
This simple around each life-cycle closure will rollback ALL my spec's executions even if they throw exceptions.
TestBox since v2.3.0 also allows you to declare life-cycle methods via annotations. Please see our Annotations section for more information.
When you use beforeEach()
, afterEach()
, and aroundEach()
at the same time, there is a specific order they fire in. For a given describe block, they will fire in this order. Remember, aroundEach()
is split into two parts-- the half of the method before you call spec.body()
and the second half of the method.
beforeEach
aroundEach (first half)
it() (the spec.body()
call)
aroundEach (second half)
afterEach()
Here's an example:
If there are more than one it()
blocks, the process repeats for each one. Steps 1, 2, 4, 5 will wrap every single it()
.
When you nest more than one describe block inside the other, the before/around/after order is the same but drills down to the innermost describe and then bubbles back up. That means the outermost beforeEach()
starts and we end on the outermost afterEach()
.
Here's what an example flow would look like that had before/after/around specified in two levels of describes with a single it()
in the inner most describe.
Outermost beforeEach()
call
Innermost beforeEach()
call
Outermost aroundEach()
call (first half)
Innermost aroundEach()
call (first half)
The it()
block
Innermost aroundEach()
calls (second half)
Outermost aroundEach()
call (second half)
Innermost afterEach()
call
Outermost afterEach()
call
This works regardless of the number of levels and can obviously have many permutations, but the basic order is still the same. Before/around/after and starting at the outside working in, and back out again. This process happens for every single spec or it()
block. This is as opposed to the beforeAll()
and afterAll()
method which only run once for the entire CFC regardless of how many specs there are.
Specs and suites can be skipped from execution by prefixing certain functions with the letter x
or by using the skip argument in each of them. The reporters will show that these suites or specs where skipped from execution. The functions you can prefix are:
it()
describe()
story()
given()
when()
then()
feature()
The skip
argument can be a boolean value or a closure. If the value is true then the suite or spec is skipped. If the return value of the closure is true then the suite or spec is skipped. Using the closure approach allows you to dynamically at runtime figure out if the desired spec or suite is skipped. This is such a great way to prepare tests for different CFML engines.
TestBox not only provides you with global life-cycle methods but also with localized test methods. This is a great way to keep your tests DRY (Do not repeat yourself)! TestBox provides the setup( currentMethod ) and teardown( currentMethod ) methods that each receives the name of the test method in question. which as their names indicate, they execute before a test and after a test in a test bundle.
Tests and suites can be tagged with TestBox labels. Labels allows you to further categorize different tests or suites so that when a runner executes with labels attached, only those tests and suites will be executed, the rest will be skipped. Labels can be applied globally to the component declaration of the test bundle suite or granularly at the test method declaration.
Please refer to our MockBox section to take advantage of all the mocking and stubbing you can do. However, every BDD TestBundle has the following functions available to you for mocking and stubbing purposes:
makePublic( target, method, newName )
- Exposes private methods from objects as public methods
querySim( queryData )
- Simulate a query
getMockBox( [generationPath] )
- Get a reference to MockBox
createEmptyMock( [className], [object], [callLogging=true])
- Create an empty mock from a class or object
createMock( [className], [object], [clearMethods=false], [callLogging=true])
- Create a spy from an instance or class with call logging
prepareMock( object, [callLogging=true])
- Prepare an instance of an object for method spies with call logging
createStub( [callLogging=true], [extends], [implements])
- Create stub objects with call logging and optional inheritance trees and implementation methods
getProperty( target, name, [scope=variables], [defaultValue] )
- Get a property from an object in any scope
TestBox discovers test methods in your bundle CFC by applying the following discovery rules:
Any method that has a test annotation on it
Any public method that starts or ends with the word test
Each test method will test the state of the SUT (software under test) or sometimes referred to as code under test. It will do so by asserting that actual values from an execution match an expected value or condition. TestBox offers an assertion library that you have available in your bundle via the injected variable $assert. You can also use our expectations library if you so desire, but that is mostly used in our BDD approach.
Each test function can also have some cool annotations attached to it.
TestBox comes also with a nice plethora of reporters:
ANTJunit : A specific variant of JUnit XML that works with the ANT junitreport task
Codexwiki : Produces MediaWiki syntax for usage in Codex Wiki
Console : Sends report to console
Doc : Builds semantic HTML to produce nice documentation
Dot : Builds an awesome dot report
JSON : Builds a report into JSON
JUnit : Builds a JUnit compliant report
Raw : Returns the raw structure representation of the testing results
Simple : A basic HTML reporter
Text : Back to the 80's with an awesome text report
XML : Builds yet another XML testing report
Tap : A test anything protocol reporter
Min : A minimalistic view of your test reports
MinText : A minimalistic view of your test reports for consoles
NodeJS : User-contributed:
Tests and suites can be skipped from execution by using the skip annotation in the component or function declaration. The reporters will show that these suites or tests where skipped from execution. The value of the skip annotation can be a simple true or false or it can be the name of a UDF that exists in the same bundle CFC. This UDF must return a boolean value and it is evaluated at runtime.
TestBox ships with several test runners internally but we have tried to simplify and abstract it with our TestBox object which can be found in the testbox.system package. The TestBox object allows you to execute tests from a CFC, CFM, HTTP, NodeJS, SOAP or REST. You can also make your CFC's extend from our BaseSpec
class so you can execute it directly via the URL. The main execution methods are:
Info We encourage you to read the API docs included in the distribution for the complete parameters for each method.
run()
ArgumentsHere are the arguments you can use for initializing TestBox or executing the run()
method
runRemote()
argumentsHere are the arguments you can use for executing the runRemote()
method:
The bundles argument which can be a single CFC path or an array of CFC paths or a directory argument so it can go and discover the test bundles from that directory. The reporter argument can be a core reporter name like: json,xml,junit,raw,simple,dots,tap,min,etc or it can be an instance of a reporter CFC. You can execute the runners from any cfm template or any CFC or any URL, that is up to you.
TestBox ships with a global runner that can be used to run pretty much anything. You can customize it or place it wherever you need it:
TestBox ships with a test browser that is highly configurable to whatever URL accessible path you want. It will then show you a test browser where you can navigate and execute not only individual tests, but also directory suites as well.
In our test samples and templates we include an ANT runner that will be able to execute your tests via ANT. It can also leverage our ANTJunit reporter to use the junitreport task to produce JUnit compliant reports as well. You can find this runner in the test samples and runner template directory.
You can run tests via SOAP by leveraging the runRemote() method. The WSDL URL will be
HTTP/REST Runner You can run tests via HTTP/REST by leveraging the runRemote() endpoint. The URL will be
There is a user-contributed NodeJS Runner that looks fantastic and can be downloaded here:
Just use node to install:
No matter what style you decide to use, you will still end up building a Testing Bundle CFC. This CFC will either contain BDD style suites and specs or xUnit style method tests. These components are simple components with no inheritance that can contain several different life-cycle callback methods. Internally we do the magic of making this CFC inherit all capabilities and methods from our BaseSpec class (testbox.system.BaseSpec
).
Caution We highly encourage you to use the inheritance approach so you can get introspection, faster execution and ability to execute CFC directly.
You can tag a bundle component declaration with the boolean asyncAll
annotation and TestBox will execute all specs in separate threads for you concurrently.
Caution Once you delve into the asynchronous world you will have to make sure your tests are also thread safe (var-scoped) and provide any necessary locking.
At runtime we provide the inheritance via mixins so you don't have to worry about it. However, if you want to declare the inheritance you can do so and this will give you the following benefits:
Some IDEs will be able to give you introspection for methods and properties
You will be able to use the HTML runner by executing directly the runRemote method on the CFC Bundle
Your tests will run faster
TestBox comes with two flavors of testing: and .
BDD stands for behavior driven development and is highly based on creating specifications and expectations of results in a readable DSL (Domain Specifc Language).
xUnit style of testing is the more traditional TDD or test driven development approach where you are creating a test case CFC that matches the software under test and for each method in the SUT you create a test method in the test case CFC. We cover these two topics in our primers chapter (Chapter 3).
No matter what testing style you pick you, TestBox will execute the tests for you and then send your results to a reporter that can produce awesome testing reports for you.
If you make your test bundle CFC inherit from our testbox.system.BaseSpec class, you will be able to execute the CFC directly via the URL: ```javascript ``` You can also pass the following arguments to the method via the URL: * **testSuites** : A list or array of suite names that are the ones that will be executed ONLY! * **testSpecs** : A list or array of test names that are the ones that will be executed ONLY! * **reporter** : The type of reporter to run the test with ```javascript ```
Argument
Required
Default
Type
Description
labels
false
---
string/list
The list of labels this test belongs to
skip
false
false
boolean/udf
A boolean flag that makes the runners skip the test for execution. It can also be the name of a UDF in the same CFC that will be executed and MUST return a boolean value.
Argument | Required | Default | Type | Description |
bundles | true | --- | string | The path, list of paths or array of paths of the spec bundle CFCs to run and test |
directory | false | --- | string | The directory mapping to test: directory = the path to the directory using dot notation (myapp.testing.specs) |
recurse | false | true | boolean | Recurse the directory mapping or not, by default it does |
reporter | false | simple | string/path | The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or a class path to the reporter to use. |
reporterOptions | false | {} | JSON | A JSON struct literal of options to pass into the reporter |
labels | false | false | string | The string array of labels the runner will use to execute suites and specs with. |
excludes | false | --- | string/array | The string or array of labels to exclude from running |
options | false | {} | JSON | A JSON struct literal of configuration options that are optionally used to configure a runner. |
testBundles | false | --- | string/array | A list or array of bundle names that are the ones that will be executed ONLY! |
testSuites | false | --- | string | A list of suite names that are the ones that will be executed ONLY! |
testSpecs | false | --- | string | A list of test names that are the ones that will be executed ONLY |
Argument | Required | Default | Type | Description |
bundles | true | --- | string/array | The path, list of paths or array of paths of the spec bundle CFCs to run and test |
directory | false | --- | struct | The directory mapping path or a struct: [ mapping = the path to the directory using dot notation (myapp.testing.specs), recurse = boolean, filter = closure that receives the path of the CFC found, it must return true to process or false to continue process ] |
reporter | false | simple | string/struct/instance | The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or an instance of a coldbox.system.reports.IReporter. You can also pass a struct with [type="string or classpath", options={}] if a reporter expects options. |
labels | false | false | string/array | The string or array of labels the runner will use to execute suites and specs with. |
excludes | false | --- | string/array | The string or array of labels to exclude from running |
options | false | {} | struct | A structure of property name-value pairs that each runner can implement and use at its discretion. |
testBundles | false | --- | string/array | A list or array of bundle names that are the ones that will be executed ONLY! |
testSuites | false | --- | string/array | A list or array of suite names that are the ones that will be executed ONLY! |
testSpecs | false | --- | string/array | A list or array of test names that are the ones that will be executed ONLY |
beforeTests()
- Executes once before all tests for the entire test bundle CFC
afterTests()
- Executes once after all tests complete in the test bundle CFC
setup( currentMethod )
- Executes before every single test case and receives the name of the actual testing method
teardown( currentMethod )
- Executes after every single test case and receives the name of the actual testing method
At runtime, TestBox will also decorate your bundle CFC with tons of methods to help you in your testing adventures. Depending on your testing style you will leverage some more than others. For the latest methods please visit the API Docs for more information.
beforeAll()
- Executes once before all specs for the entire test bundle CFC
afterAll()
- Executes once after all specs complete in the test bundle CFC
run( testResults, TestBox )
- Executes once so it can capture all your describe and it blocks
beforeEach( body, data )
- Executes before every single spec in a single describe block and receives the currently executing spec.
afterEach( body, data )
- Executes after every single spec in a single describe block and receives the currently executing spec.
aroundEach( body, data )
- Executes around the executing spec so you can provide code surrouding the spec.
The great flexibility of the BDD approach is that it allows you to nest describe
blocks or create multiple describe
blocks. Each describe
block can have its own life-cycle methods as well. Not only that, if they are nested, TestBox will walk the tree and call each beforeEach()
and afterEach()
in the order you declare them.
You can pass in an argument called data
which is a struct
of dynamic data to pass into the life-cycle method. You can then pickup this data in the closure for the life-cycle.
Here is a typical example:
Test suites are a collection of testing methods or specifications (specs) inside of a testing bundle CFC. Let's investigate the styles.
In addition to the life-cycle methods, you can make any method a life-cycle method by giving it the desired annotation in its function definition. This is especially useful for parent classes that want to hook in to the TestBox life-cycle.
@beforeAll
- Executes once before all specs for the entire test bundle CFC
@afterAll
- Executes once after all specs complete in the test bundle CFC
@beforeEach
- Executes before every single spec in a single describe block and receives the currently executing spec.
@afterEach
- Executes after every single spec in a single describe block and receives the currently executing spec.
@aroundEach
- Executes around the executing spec so you can provide code surrounding the spec.
Below are several examples using script notation.
DBTestCase.cfc (parent class)
PostsTest.cfc
This also helps parent classes enforce their setup methods are called by annotating the methods with @beforeAll
. No more forgetting to call super.beforeAll()
!
Info: You can have as many annotated methods as you would like. TestBox discovers them up the inheritance chain and calls them in reverse order.
Each style has its own life-cycle methods you can discover in their primers. They are callbacks that TestBox calls at specific points in time of testing. They are a great way to setup or teardown things before the entire testing suite or one test case. Here is a short synopsis for each style.
At runtime, TestBox will inject several public variables into your testing bundle CFCs to help you with your testing:
$mockbox
: A reference to MockBox
$assert
: A reference to our Assertions library
$utility
: A utility CFC
$customMatchers
: A collection of custom matchers registered
$exceptionAnnotation
: The annotation used to discover expected exceptions, defaults to expectedException
$testID
: A unique identifier for the test bundle
$debugBuffer
: The debugging buffer stream
TestBox supports the concept of assertions to allow for validations and for legacy tests. We encourage developers to use our BDD expectations as they are more readable and fun to use (Yes, fun I said!).
The assertions are modeled in the class testbox.system.Assertion
, so you can visit the API for the latest assertions available. Each test bundle will receive a variable called $assert
which represents the assertions object. Here are some common assertion methods:
The toBe()
matcher represents an equality matcher much how an $assert.isEqual()
behaves. Below are several of the most common matchers available to you. However, the best way to see which ones are available is to checkout the API Docs.
TestBox allows you to create BDD expectations with our expectations and matcher API DSL. You start by calling our expect()
method, usually with an actual value you would like to test. You then concatenate the expectation of that actual value/function to a result or what we call a matcher. You can also concatenate matchers (as of v2.1.0) so you can provide multiple matching expectations to a single value.
Our default syntax for expecting exceptions is to use our closure approach concatenated with our toThrow()
method in our expectations or our throws()
method in our assertions object.
Info Please always remember to pass in a closure to these methods and not the actual test call:
function(){ myObj.method();}
Example
This will execute the closure in a nested try/catch
block and make sure that it either threw an exception, threw with a type, threw with a type and a regex match of the exception message. If you are in an environment that does not support closures then you will need to create a spec testing function that either uses the expectedException
annotation or function call:
Caution Please note that the usage of the
expectedException()
method can ONLY be used while in synchronous mode. If you are running your tests in asynchronous mode, this will not work. We would recommend the closure or annotation approach instead.
You can prefix your expectation with the not
operator to easily cause negative expectations for any matcher. When you read the API Docs or the source, you will find nowhere the not methods. This is because we do this dynamically by convention.
TestBox comes with a plethora of assertions that cover what we believe are common scenarios. However, we recommend that you create custom assertions that meet your needs and criteria so that you can avoid duplication and have re-usability. A custom assertion function can receive any amount of arguments but it must use the fail()
method in order to fail an assertion or just return true or void for passing.
Here is an example:
You can register assertion functions in several ways within TestBox, but we always recommend that you register them inside of the beforeTests()
or setup()
life-cycle method blocks, so they are only inserted once.
You can pass a structure of key/value pairs of the assertions you would like to register. The key is the name of the assertion function and the value is the closure function representation.
After it is registered, then you can just use it out of the $assert
object it got mixed into.
You can also store a plethora of assertions (Yes, I said plethora), in a CFC and register that as the assertions via its instantiation path. This provides much more flexibility and re-usability for your projects.
You can also register more than 1 CFC by using a list or an array:
Here is the custom assertions CFC source:
TestBox comes with a decent amount of matchers that cover what we believe are common scenarios. However, we recommend that you create custom matchers that meet your needs and criteria so that you can avoid duplication and have re-usability.
Every custom matcher is a function and must have the following signature, with MyMatcher
being the name of your custom matcher function:
The matcher function receives the expectation
object and a second argument which is a structure of all the arguments with which the matcher function was called with. It must then return a true or a false depending if it passes your criteria. It will most likely use the expectation
object to retrieve the actual and isNot values. It can also set a custom failure message on the expectation object itself by using the message
property of the expectation
object.
The next step is to tell TestBox about your matcher.
You can register matcher functions in several ways within TestBox, but we always recommend that you register them inside of the beforeAll()
or beforeEach()
life-cycle method blocks for performance considerations and global availability.
You can pass a structure of key\/value pairs of the matchers you would like to register. The key is the name of the matcher function and the value is the closure function representation.
After it is registered, then you can use it.
You can also store a plethora of matchers (Yes, I said plethora), in a CFC and register that as the matchers via its instantiation path. This provides much more flexibility and re-usability for your projects.
You can also register a CFC instance:
Sometimes you will need to produce output from your tests and you can do so elegantly via some functions we have provided that are available in your test bundles:
console(var, top=[9999])
- Send data to the console via writeDump( output="console" )
debug(var, label="", deepCopy=[false], top=999)
- Send data to the TestBox debug stream, will be saved in the test results and outputted in reports
print( message )
- Write some output to the ColdFusion output buffer
println( message )
- Write some output to the ColdFusion output buffer and appends a newline character
These are great little utilities that are needed to send output to several locations.
Hint Please note that the
debug()
method does NOT do deep copies by default.
A test suite begins with a call to our TestBox describe()
function with at least two arguments: a title
and a body
closure within the life-cycle method called run()
. The title
is the name of the suite to register and the body
function is the block of code that implements the suite. There are more arguments which you can see below:
In BDD, suites can be nested within each other which provides a great capability of building trees of tests. Not only does it arrange them in tree format but also TestBox will execute the life-cycle methods in order of nested suites as it traverses the tree.
Specs are defined by calling the TestBox it()
global function, which takes in a title and a function. The title is the title of this spec or test you will write and the function is a block of code that represents the test/spec. A spec will contain most likely one or more expectations that will test the state of the SUT (software under test) or sometimes referred to as code under test.
An expectation is a nice assertion DSL that TestBox exposes so you can pretty much read what should happen in the testing scenario. A spec will pass if all expectations pass. A spec with one or more expectations that fail will fail the entire spec.
The testing bundle CFC is actually the suite in xUnit style as it contains all the test methods you would like to test with. Usually, this CFC represents a test case for a specific software under test (SUT), whether that's a model object, service, etc. This component can have some cool annotations as well that can alter its behavior.
Caution If you activate the
asyncAll
flag for asynchronous testing, you HAVE to make sure your tests are also thread safe and appropriately locked.
TestBox discovers test methods in your bundle CFC by applying the following discovery rules:
Any method that has a test
annotation on it
Any public method that starts or ends with the word test
Each test method will test the state of the SUT (software under test) or sometimes referred to as code under test. It will do so by asserting that actual values from an execution match an expected value or condition. TestBox offers an assertion library that you have available in your bundle via the injected variable $assert
. You can also use our expectations library if you so desire, but that is mostly used in our BDD approach (chapter 3: Primers).
Each test function can also have some cool annotations attached to it.
TestBox ships with several test runners internally but we have tried to simplify and abstract it with our TestBox object which can be found in the testbox.system package
. The testbox
object allows you to execute tests from a CFC, CFM, HTTP, SOAP, NodeJS or REST. You can also make your CFC's extend from our BaseSpec
class so you can execute it directly via the URL. The main execution methods are:
We encourage you to read the API docs included in the distribution for the complete parameters for each method.
run()
ArgumentsHere are the arguments you can use for initializing TestBox or executing the run()
method
runRemote()
ArgumentsHere are the arguments you can use for executing the runRemote()
method of the TestBox object:
The bundles
argument which can be a single CFC path or an array of CFC paths or a directory argument so it can go and discover the test bundles from that directory.
The reporter
argument can be a core reporter name like: json,xml,junit,raw,simple,dots,tap,min,etc or it can be an instance of a reporter CFC.
You can execute the runners from any cfm template or any CFC or any URL, that is up to you.
TestBox ships with a global runner that can be used to run pretty much anything. You can customize it or place it wherever you need it:
You can run tests via SOAP by leveraging the runRemote()
method. The WSDL URL will be
If you make your test bundle CFC inherit from our testbox.system.BaseSpec
class, you will be able to execute the CFC directly via the URL:
You can also pass the following arguments to the method via the URL:
testSuites
: A list or array of suite names that are the ones that will be executed ONLY!
testSpecs
: A list or array of test names that are the ones that will be executed ONLY!
reporter
: The type of reporter to run the test with
You can run tests via HTTP/REST by leveraging the runRemote()
endpoint. The URL will be
TestBox ships with a test browser that is highly configurable to whatever URL accessible path you want. It will then show you a test browser where you can navigate and execute not only individual tests, but also directory suites as well.
Argument
Required
Default
Type
Description
title
true
---
string
The title of the suite to register
body
true
---
closure/udf
The closure that represents the test suite
labels
true
---
string/array
The list or array of labels this suite group belongs to
asyncAll
false
false
Boolean
If you want to parallelize the execution of the defined specs in this suite group.
skip
false
false
Boolean
A flag or a closure that tells TestBox to skip this suite group from testing if true. If this is a closure it must return boolean.
Argument
Required
Default
Type
Description
title
true
---
string
The title of the spec
body
true
---
closure/udf
The closure that represents the spec
labels
false
---
string/array
The list or array of labels this suite group belongs to
skip
false
false
Boolean
A flag or a closure that tells TestBox to skip this suite group from testing if true. If this is a closure it must return boolean.
data
false
{}
struct
A struct of data you can bind the spec with so you can use within the body
closure
Argument
Required
Default
Type
Description
displayName
false
--
string
If used, this will be the name of the test suite in the reporters.
asyncAll
false
false
boolean
If true, it will execute all the test methods in parallel and join at the end asynchronously.
labels
false
---
string/list
The list of labels this test belongs to
skip
false
false
boolean/udf
A boolean flag that makes the runners skip the test for execution. It can also be the name of a UDF in the same CFC that will be executed and MUST return a boolean value.
Arguments
Required
Default
Type
Description
labels
false
string/list
---
The list of labels this test belongs to
skip
false
false
boolean/udf
A boolean flag that makes the runners skip the test for execution. It can also be the name of a UDF in the same CFC that will be executed and MUST return a boolean value.
Argument
Required
Default
Type
Description
bundles
true
---
string/array
The path, list of paths or array of paths of the spec bundle CFCs to run and test
directory
false
---
struct
The directory mapping path or a struct: [ mapping = the path to the directory using dot notation (myapp.testing.specs), recurse = boolean, filter = closure that receives the path of the CFC found, it must return true to process or false to continue process ]
reporter
false
simple
string/struct/instance
The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or an instance of a coldbox.system.reports.IReporter. You can also pass a struct with [type="string or classpath", options={}] if a reporter expects options.
labels
false
false
string/array
The string or array of labels the runner will use to execute suites and specs with.
excludes
false
---
string/array
The string or array of labels to exclude from running
options
false
{}
struct
A structure of property name-value pairs that each runner can implement and use at its discretion.
testBundles
false
---
string/array
A list or array of bundle names that are the ones that will be executed ONLY!
testSuites
false
---
string/array
A list or array of suite names that are the ones that will be executed ONLY!
testSpecs
false
---
string/array
A list or array of test names that are the ones that will be executed ONLY
callbacks
false
{}
struct of closures or CFC
A struct of listener callbacks or a CFC with callbacks for listening to progress of the testing: onBundleStart,onBundleEnd,onSuiteStart,onSuiteEnd,onSpecStart,onSpecEnd
eagerFailure
false
false
boolean
If true, then after testing a bundle if there are any failures or errors no more testing will be performed.
Argument
Required
Default
Type
Description
bundles
true
---
string
The path, list of paths or array of paths of the spec bundle CFCs to run and test
directory
false
---
string
The directory mapping to test: directory = the path to the directory using dot notation (myapp.testing.specs)
recurse
false
true
boolean
Recurse the directory mapping or not, by default it does
reporter
false
simple
string/path
The type of reporter to use for the results, by default is uses our 'simple' report. You can pass in a core reporter string type or a class path to the reporter to use.
reporterOptions
false
{}
JSON
A JSON struct literal of options to pass into the reporter
labels
false
false
string
The string array of labels the runner will use to execute suites and specs with.
excludes
false
---
string/array
The string or array of labels to exclude from running
options
false
{}
JSON
A JSON struct literal of configuration options that are optionally used to configure a runner.
testBundles
false
---
string/array
A list or array of bundle names that are the ones that will be executed ONLY!
testSuites
false
---
string
A list of suite names that are the ones that will be executed ONLY!
testSpecs
false
---
string
A list of test names that are the ones that will be executed ONLY
eagerFailure
false
false
boolean
If true, then after testing a bundle if there are any failures or errors no more testing will be performed.
TestBox is fully compliant with MXUnit xUnit test cases. In order to leverage it you will need to create or override the /mxunit
mapping and make it point to the /testbox/system/compat
folder. That's it, everything should continue to work as expected.
Note you will still need TestBox to be in the web root, or have a
/testbox
mapping created even when using the MXUnit compat runner.
After this, all your test code remains the same but it will execute through TestBox's xUnit runners. You can even execute them via the normal URL you are used to. If there is something that is not compatible, please let us know and we will fix it.
We also support in the compatibility mode the expected exception MXUnit annotation: mxunit:expectedException
and the expectException()
methods. The expectException()
method is not part of the assertion library, but instead is inherited from our BaseSpec.cfc
.
Please refer to MXUnit's documentation on the annotation and method for expected exceptions, but it is supported with one caveat. The expectException()
method can produce unwanted results if you are running your test specs in TestBox asynchronous mode since it stores state at the component level. Only synchronous mode is supported if you are using the expectException()
method. The annotation can be used in both modes.
You can also build your own reporters by implementing our core class: testbox.system.reporters.IReport
Once you implement your own report you just need to pass the class path or the instance of your reporter to the TestBox runner methods using the reporter
argument. The reporter
argument can be the following values:
string
- The class path of your reporter
instance
- The instance of your reporter CFC
struct
- A structure representing your reporter with the following keys: { type="class_path", options={}
. This is mostly used if you want to instantiate and use your reporter with a structure of options.
Now you can init TestBox with your reporter:
Here is a sample reporter for you that generates JSON for the output.
In our test samples and templates we include an ANT runner that will be able to execute your tests via ANT. It can also leverage our ANTJunit reporter to use the junitreport task to produce JUnit compliant reports as well. You can find this runner in the test samples and runner template directory.
This build is our global build for running TestBox via ANT.
MockBox can be downloaded from http://www.ortussolutions.com/products/testbox or can be installed via CommandBox as part of the TestBox package.
The approach that we take with MockBox is a dynamic and minimalistic approach. Why dynamic? Well, because we dynamically transform target objects into mock form at runtime. The API for the mocking factory is very easy to use and provides you a very simplistic approach to mocking.
We even use $()style
method calls so you can easily distinguish when using or mocking methods, properties, etc. So what can MockBox do for me?
Create mock objects for you and keep their methods intact (Does not wipe methods, so you can do method spys, or mock helper methods)
Create mock objects and wipe out their method signatures
Create stub objects for objects that don't even exist yet. So you can build to interfaces and later build dependencies.
Decorate instantiated objects with mocking capabilities (So you can mock targeted methods and properties; spys)
Mock internal object properties, basically do property injections in any internal scope
State-Machine Results. Have a method recycle the results as it is called consecutively. So if you have a method returning two results and you call the method 4 times, the results will be recycled: 1,2,1,2
Method call counter, so you can keep track of how many times a method has been called
Method arguments call logging, so you can keep track of method calls and their arguments as they are called. This is a great way to find out what was the payload when calling a mocked method
Ability to mock results depending on the argument signatures sent to a mocked method with capabilities to even provide state-machine results
Ability to mock private/package methods
Ability to mock exceptions from methods or make a method throw a controlled exception
Ability to change the return type of methods or preserve their signature at runtime, extra cool when using stubs that still have no defined signature
Ability to call a debugger method ($debug()) on mocked objects to retrieve extra debugging information about its mocking capabilities and its mocked calls
MockBox has been designed to work under the following CFML Engines:
Railo 3.1+ (Deprecated)
Lucee 4.5+
Adobe ColdFusion 9+
Write capabilities on disk for the default path of /{mockbox}/system/testings/stubs
. You can also choose the directory destination for stub creations yourself when you initialize MockBox. If using ColdFusion 9 or Railo you can even use ram://
and use the virtual file system.
The factory takes in one constructor argument that is not mandatory: generationPath
. This path is a relative path of where the factory generates internal mocking stubs that are included later on at runtime. Therefore, the path must be a path that can be used using cfinclude
. The default path the mock factory uses is the following, so you do not have to specify one, just make sure the path has WRITE permissions:
Hint If you are using Lucee or ACF10+ you can also decide to use the
ram://
resource and place all generated stubs in memory.
In order to create a mock object you need to use any of the following methods: createMock()
, createEmptyMock()
, or prepareMock()
.
Used to create a new mock object from scratch or from an already instantiated object.
Parameters:
className - The class name of the object to create and mock
object - The instantiated object to add mocking capabilities to, similar to using prepareMock()
clearMethods - If true, all methods in the target mock object will be removed. You can then mock only the methods that you want to mock
callLogging - Add method call logging for all mocked methods only
Used to create a new mock object with all its method signatures wiped out, basically an interface with no real implementation. It will be up to you to mock all behavior.
Parameters:
className - The class name of the object to create and mock
object - The instantiated object to add mocking capabilities to, similar to using prepareMock()
callLogging - Add method call logging for all mocked methods only
Decorate an already instantiated object with mocking capabilities. It does not wipe out the object's methods or signature, it only decorates it (mixes-in methods) with methods for mocking operations. This is great for doing targeted mocking for specific methods, private methods, properties and more.
Parameters:
object - The already instantiated object to prepare for mocking
callLogging - Add method call logging for all mocked methods only
Caution If call logging is turned on, then the mock object will keep track of all method calls to mocked methods ONLY. It will store them in a sequential array with all the arguments the method was called with (named or ordered). This is essential if you need to investigate if a method was called and with what arguments. You can also use this to inspect save or update calls based on mocked external repositories.
Sample:
Let's say that we have a user service layer object that relies on the following objects:
sessionstorage - a session facade object
transfer - the transfer ORM
userDAO - a data access object for complex query operations
We can start testing our user service and mocking its dependencies by preparing it in a test case CFC with the following setup()
method:
The service CFC we just injected mocked dependencies:
TestBox comes also with a nice plethora of reporters:
ANTJunit : A specific variant of JUnit XML that works with the ANT junitreport task
Codexwiki : Produces MediaWiki syntax for usage in Codex Wiki
Console : Sends report to console
Doc : Builds semantic HTML to produce nice documentation
Dot : Builds an awesome dot report
JSON : Builds a report into JSON
JUnit : Builds a JUnit compliant report
Raw : Returns the raw structure representation of the testing results
Simple : A basic HTML reporter
Text : Back to the 80's with an awesome text report
XML : Builds yet another XML testing report
Tap : A test anything protocol reporter
Min : A minimalistic view of your test reports
To use a specific reporter append the reporter
variable to the url string. ex &reporter=Text
However, you can also build custom reporters very easily.
There is a user-contributed NodeJS Runner that looks fantastic and can be downloaded here: https://www.npmjs.com/package/testbox-runner
You can use node to install as well:
MockBox is a companion package to TestBox that will give you advanced mocking/stubbing capabilities; hence a Mocking Framework. You can use it from within TestBox or as a standalone package in any other testing framework or CFML application as a data provider.
Download from our downloads page or clone via github. Also read our installation section.
"A mock object is an object that takes the place of a 'real' object in such a way that makes testing easier and more meaningful, or in some cases, possible at all". by Scott Bain (Emergent Design The Evolutionary Nature of Professional Software Development)
Here are some examples of real life mocks to get you in the mocking mood:
When doing unit testing of ColdFusion CFCs, we will come to a point where a single class can have multiple external dependencies or collaborators; whether they are classes themselves, data, external APIs, etc. Therefore, in order to unit test our class exclusively and easily we need to be able to mock this behavior or have full control of this behavior. Remember that unit testing is the testing of software units in isolation. If not, we would be instantiating and creating entire set of components, frameworks, pulling network plugs and so much more ridiculous but functional things just to test one single piece of functionality and/or behavior. So in summary, mock objects are just test oriented replacements for collaborators and dependencies.
Mock objects can also be created by hand, but MockBox takes this pain away by leveraging dynamic techniques so that you can Mock dynamically and at runtime. Like Scott Bain describes in his Emergent Design book:
"Mocks are definitely congruent with the Gang of Four (GoF) notion of designing to interfaces, because a mock is essentially the interface without any real implementation." - Scott Bain (Emergent Design)
You will be leveraging MockBox to create objects that represent your dependencies or even data, decide what methods will return (expectations), mock network connections, exceptions and much more. You can then very easily test the exclusive behavior of components as you will now have control of all expectations, and remember that testing is all about expectations. Also, as your object oriented applications get more complex, mocking becomes essential, but you have to be aware that there are limitations. Not only will you do unit-testing but you will need to expand to do integration testing to make sure the all encompassing behavior is still maintained. However, by using a mocking framework like MockBox you will be able to apply a test-driven development methodology to your unit-testing and be able to accelerate your development and testing. The more you mock, the more you will get a feel for it and find it completely essential when doing unit testing. Welcome to a world where mocking is fun and not frowned upon :)
This method is used in order to mock an internal property on the target object. Let's say that the object has a private property of userDAO that lives in the variables scope and the lifecycle for the object is controlled by its parent, in this case the user service. This means that this dependency is created by the user service and not injected by an external force or dependency injection framework. How do we mock this? Very easily by using the $property() method on the target object.
Parameters:
propertyName - The name of the property to mock
propertyScope - The scope where the property lives in. The default is variables scope.
mock - The object or data to inject and mock
Not only can you mock properties that are objects, but also mock properties that are simple/complex types. Let's say you have a property in your target object that controls debugging and by default the property is false, but you want to test the debugging capabilities of your class. So we have to mock it to true now, but the property exists in variables.instance.debugMode? No problem mate (Like my friend Mark Mandel says)!
This method is used to tell MockBox that you want to mock a method with a SPECIFIC number of argument calls. Then you will have to set the return results for it, but this is absolutely necessary if you need to test an object that makes several method calls to the same method with different arguments, and you need to mock different results coming back. Example, let's say you are using a ColdBox configuration bean that holds configuration data. You make several calls to the getKey()
method with different arguments:
How in the world can I mock this? Well, using the mock arguments method.
Hint So remember that if you use the
$args()
call, you need to tell it what kind of results you are expecting by calling the$results()
method after it or you might end up with an exception.
In order to create a stub object you will use the : createStub()
method.
public any createStub([boolean callLogging='true'], [extends], [implements])
Parameters:
callLogging
- Add method call logging for all mocked methods
extends
- Make the stub extend from certain CFC
implement
- Make the stub adhere to an interface(s)
This method will create an empty stub object that you can use and mock with methods and properties. It can then be used in any code to satisfy dependencies meanwhile you build them. This is great to start working on projects where you are relying on other teams to build functionality but you have agreed on specific data or code interfaces. It is also super fancy as it can allow you to tell the stub to inherit from another CFC and look like it, or even pass in one or more interfaces that it must implement. If they are interfaces, then MockBox will generate all the necessary methods to satisfy those interfaces.
The createStub()
method has an argument called extends
that accepts a class path. This will create and generate a stub that physically extends that class path directly. This is an amazing way to create stubs that you can override with inherited functionality or just make it look like it is EXACTLY the type of object you want.
The createStub()
method has an argument called implements
that accepts a list of interface class paths you want the stub to implement. MockBox will then generate the stub and it will make sure that it implements all the methods the interfaces have defined as per their contract. This is such a fantastic and easy way to create a stub that looks and feels and actually has the methods an interface needs.
This method can only be used in conjunction with $()
as a chained call as it needs to know for what method are the results for.
The purpose of this method is to make a method return more than 1 result in a specific repeating sequence. This means that if you set the mock results to be 2 results and you call your method 4 times, the sequence will repeat itself 1 time. MUMBO JUMBO, show me!! Ok Ok, hold your horses.
As you can see, the sequence repeats itself once the call counter increases. Let's say that you have a test where the first call to a user object's isAuthorized()
method is false but then it has to be true. Then you can do this:
This is the method that you will call upon in order to mock a method's behavior and return results. This method has the capability of mocking a return value or even making the method throw a controlled exception. By default the mocked method results will be returned all the time the method is called. So if the mocked method is called twice, the results will always be returned.
Parameters:
method
- The method you want to mock or spy on
returns
- The results it must return, if not passed it returns void or you will have to do the mockResults() chain
preserveReturnType
- If false, the mock will make the returntype of the method equal to ANY
throwException
- If you want the method call to throw an exception
throwType
- The type of the exception to throw
throwDetail
- The detail of the exception to throw
throwMessage
- The message of the exception to throw
callLogging
- Will add the machinery to also log the incoming arguments to each subsequent calls to this method
preserveArguments
- If true, argument signatures are kept, else they are ignored. If true, BEWARE with $args() matching as default values and missing arguments need to be passed too.
callback
- A callback to execute that should return the desired results, this can be a UDF or closure. It also receives all caller arguments as well.
throwErrorCode
- The error code to throw in the exception
The cool thing about this method is that it also returns the same instance of the object. Therefore, you can use it to chain calls to the object and do multiple mocking of methods all within the same line. Remember that if no returns argument is provided then the return is void
Let's do some samples now
Once you have created a mock object, you can use it like the real object as it will respond exactly as it was coded. However, you can override its behavior by using the mocking methods that have been placed on the mocked object at run-time. The methods that you can call upon in your object are the following (we will review them in detail later):
This method can help you retrieve any public or private internal state variable so you can do assertions. You can also pass in a scope argument so you can not only retrieve properties from the variables scope but from any nested structure inside of any private scope:
Parameters:
name - The name of the property to retrieve
scope - The scope where the property lives in. The default is variables scope.
Method Name | Return Type | Description |
$() | The Mock | Used to mock a method on the mock object that can return, throw or be a void method. |
$property() | The Mock | Mock a property in the object on any scope. |
$getProperty(name, scope) | any | Retrieve any public or private internal state variable so you can do assertions and more mocking. |
$results() | The Mock | Mock 1 or more results of a mock method call, must be chained to a $() or $().$args() call |
$args() | The Mock | Mock 1 or more arguments in sequential or named order. Must be called concatenated to a $() call and must be followed by a concatenated $results() call so the results are matched to specific arguments. |
querySim() | query | to denote columns. Ex: id, name 1 Luis Majano 2 Joe Louis |
TestBox 2.3.0 is a minor release with some great new functionality and tons of fixes. This release has been a great community effort as many people in the community contributed to its release.
Bug
[TESTBOX-123] - If test spec descriptor contains a comma, it can not be drilled down to run that one spec directly
[TESTBOX-140] - Allow Mocking of an Interface that implements another interface
[TESTBOX-158] - Give line number when an expectation fails or errors out
New Feature
[TESTBOX-150] - new expressive exception throwing goodness: $throws()
[TESTBOX-161] - Recursively call parent `aroundEach` functions in reverse tree format
[TESTBOX-162] - Add annotation hooks for lifecycle methods
[TESTBOX-163] - remove the TestBox tag contexts from the beginning of Failure Origins
[TESTBOX-164] - Make test harness easier for development via CommandBox
[TESTBOX-165] - Add travis build support for supporting pull requests and test matrixes
[TESTBOX-166] - Update API Docs to leverage DocBox instead
Improvement
[TESTBOX-160] - Explicitly place the instance "scope" in the variables scope due to lucee full cascade support
[TESTBOX-167] - update string buffers to string builders
Every run
and runRaw
methods now accept a callbacks
argument, which can be a CFC with the right listener methods or a struct with the right closure methods. This will allow you to listen to the testing progress and get information about it. This way you can build informative reports or progress bars.
The available callbacks are: