Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The BaseTestCase
leverages an internal virtual ColdBox application so you can do integration testing. Meaning that whenever you extend from the BaseTestCase
the virtual ColdBox will be loaded. You can tell the testing framework to NOT load it by using the this.loadColdBox
variable:
That's it! You will be able to use anything from the BaseTestCase
but the virtual application will not be loaded.
One of the best things you can do when you develop software applications is TEST! I know nobody likes it, but hey, you need to do it right? With the advocation of frameworks today, you get all these great tools to build your software applications, but how do you test your framework code. ColdBox has revolutionized testing HMVC and framework code, since you can unit test your event handlers, interceptors, model objects and even do integration testing and true BDD (Behavior Driven Development) and test your entire application with no browser at all.
ColdBox has already all the hooks in place to provide Behavior and Test Driven Development via TestBox and mocking/stubbing capabilities via MockBox.
TestBox is a testing framework for ColdFusion (CFML) that is based on BDD (Behavior Driven Development) for providing a clean obvious syntax for writing tests. It also includes MockBox for mocking and stubbing.
TestBox is included with all ColdBox templates.
TestBox already comes defined in the box.json
of all ColdBox application templates. However, if you have a custom ColdBox app or a custom template, then you can easily install it via CommandBox:
The --saveDev flag tells CommandBox to save TestBox locally only for testing purposes as it will not be used to send TestBox for production (http://commandbox.ortusbooks.com/content/packages/dependencies.html)
Please also note that CommandBox ships with tons of commands for interacting with TestBox and ColdBox testing classes. You can explore them by issuing the following commands or viewing the latest CommandBox API Docs (http://apidocs.ortussolutions.com/commandbox/current)
It might be that testing is tedious and takes time to get into the flow of Behavior/Test Driven Development. However, there are incredible benefits to testing:
Can improve code quality
Quick error discovery
Code confidence via immediate verification
Can expose high coupling
Encourages refactoring to produce testable code
Testing is all about behavior and expectations
Our recommendation is to do BDD and integration testing first. Unit testing is important but it is more important to verify that your requirements are met.
The execute()
method has an argument called renderResults
which defaults to false. If you pass in true then ColdBox will go through the normal rendering procedures and save the results in a request collection variable called: cbox_rendered_content
and expose to you a method in the request context called getRenderedContent()
. It will even work with renderData()
or if you are returning RESTful information. Then you can easily assert what the content would have been for an event.
getRenderedContent()
In testing mode, the ColdBox request context event
object has a convenience method called getRenderedContent()
that will give you the rendered content instead of you going directly to the request context and finding the cbox_rendered_content
variable.
Here are the annotations you can add to your testing bundle CFC.
Examples
Caution The
AppMapping
setting is the most important one. This is how your test connects to a location of a ColdBox application to test.
We will begin our adventure with integration testing. Integration testing allows you to test a real life request to your application without using a browser. Your test bundle CFC will load a new virtual application for you to test each specification under it; all aspects of your application are loaded: caching, dependency injection, AOP, etc. Then you can target an event to test and verify its behavior accordingly. First of all, these type of tests go in your integration
folder of your test harness or in the specific module folder if you are testing modules.
Here are the basics to follow for integration testing:
Create one test bundle CFC for each event handler you would like to test or base it off your BDD requirements
Bundle CFC inherits from coldbox.system.testing.BaseTestCase
The bundle CFC can have some annotations that tell the testing framework to what ColdBox application to connect to and test
Execution of the event is done via the execute()
method, which returns a request context object
Most verifications and assertions are done via the contents of the request context object (request collections)
We will explain later the life-cycle methods and the run()
method where you will be writing your specs.
Hint Please refer to our BDD primer to start: http://testbox.ortusbooks.com/content/primers/bdd/index.html
Also remember that you can use CommandBox to generate integration tests with a few simple commands:
Info Please also note that whenever you create a handler, interceptor or model with CommandBox it will automatically create the integration or unit test for you.
Every ColdBox application template comes with a nice test harness inside of a tests
folder.
Here is a breakdown of what it contains:
Application.cfc
- A unique application file for your test harness. It should mimic exactly the one in your root application folder
resources
- Some basic testing resources or any of your own testing resources
results
- Where automated results are archived
runner.cfm
- The HTML runner for your test bundles
specs
- Where you will be writing your testing bundle specs for integration testing, unit testing and module testing.
test.xml
- Your ANT runner
The Application.cfc
for your tests is extremly important as it should mimic your applications real Application.cfc
.
Please note that we provide already a mapping to your root application via /root
. We would recommend you add any ORM specs here or any other mappings here as well.
Tip: Make sure all the same settings and configs from your root Application.cfc
are replicated in your tests Application.cfc
Before we begin our adventures in testing, let's review what classes does ColdBox give you for testing and where you can find them. From the diagram you can see that our pivot class for testing is the TestBox BaseSpec
class.
From that super class we have our own ColdBox BaseTestCase
which is our base class for any testing in ColdBox and the class used for Integration Testing. We then spawn several child classes for targeted testing of different objects in your ColdBox applications:
BaseTestCase
- Used for Integration Testing
BaseModelTest
- Used for model object unit testing
BaseInterceptorTest
- Used for interceptor unit testing
BaseHandlerTest
- Used for isolated handler unit testing
ColdBox testing leverages TestBox's testing life-cycle events (http://testbox.ortusbooks.com/content/life-cycle_methods/index.html) in order to prepare the virtual ColdBox application, request context and then destroy it. For performance, a virtual application is loaded for all test cases contained within a test bundle CFC via the beforeAll()
and destroyed under afterAll()
.
Important If you override any of these methods and do not funnel the super call, then you might get cached or unexpected results.
The default for integration testing is that the virtual ColdBox application will be destroyed or unloaded in each test. To keep the virtual application accross requests you will need to use the unloadColdBox=false
annotation or the this.unloadColdBox=false
setting in your beforeAll()
method. This will stop the testing classes from destroying ColdBox and improving performance.
The execute()
method is your way of making requests in to your ColdBox application. It can take the following parameters:
Here is the integration test for the Main
handler.
The base test cases all inherit from TestBox so here are a few of the common methods you can find in every test bundle CFC ():
You can test interceptors directly with no need of doing integration testing via the BaseInterceptorTest
. This way you can unit test interceptors in isolation. All you need to do is the following:
This testing support class will create your interceptor, and decorate with mocking capabilities via MockBox and mock all the necessary companion objects around interceptors. The following are the objects that are placed in the variables scope for you to use:
interceptor : The target interceptor to test
mockController : A mock ColdBox controller in use by the target interceptor
mockRequestService : A mock request service object
mockLogger : A mock logger class in use by the target interceptor
mockLogBox : A mock LogBox class in use by the target interceptor
mockFlash : A mock flash scope in use by the target interceptor
All of the mock objects are essentially the dependencies of interceptor objects. You have complete control over them as they are already mocked for you.
You can test all your model objects directly with no need of doing integration testing. This way you can unit test model objects very very easily using great mocking capabilities. All you need to do is the following:
This testing support class will create your model object, and decorate with mocking capabilities via MockBox and create some mocking classes you might find useful in your model object unit testing. The following are the objects that are placed in the variables scope for you to use:
model : The target model object to test
mockLogger : A mock logger class
mockLogBox : A mock LogBox class
mockCacheBox : A mock Cache Factory class
mockWireBox : A mock WireBox Injector class
Caution We do not initialize your model objects for you. This is your job as you might need some mocking first.
Basic Setup
Let's use a sample event handler so we can test it:
I can test this entire handler without me building any views yet. I can even test the relocations that happen via setNextEvent()
. ColdBox will wire itself up with some mocking classes to intercept those relocations for you and place those values in the request collection for you so you can assert them. It creates a key called setnextevent in the request collection and any arguments passed to the method are also saved as keys with the following pattern:
Caution Any relocation produced by the framework via the
setNextEvent
method will produce some variables in the request collection for you to verify relocations.
Here are some useful tips for you when doing testing with ColdBox Applications:
If you are using relative paths in your application, you might encounter problems since the running application is different from the test application. Try to always use paths based on the application's AppMapping
Always use setNextEvent()
for relocations so they can be mocked
Leverage querySim()
for query mocking
Leverage for mocking and stubbing
Integration tests are NOT the same as handler tests. Handler tests will just test the handler CFC in isolation, so it will be your job to mock everything around it.
You can extend the coldbox.system.testing.BaseModelTest
to test any domain object
The has over 5,000 tests, mocking scripts and more for you to learn from
Here is a spec written for you. Please note that in the beforeEach()
life-cycle method you need to execute the setup()
method will will setup a new ColdBox request for each spec you run.
Annotation
Type
Required
Default
Description
appMapping
string
false
/
The application mapping of the ColdBox application to test. By defaults it maps to the root. Extermely important this mapping is a slash notation that points to the root of the ColdBox application to test.
configMapping
string
false
{appMapping}/config/Coldbox.cfc
The configuration file to load for this test, which by convention uses the same configuration as the application uses. This is a dot notation path to a configuration CFC.
coldboxAppKey
string
false
cbController
The named key of the ColdBox controller that will be placed in application scope for you to simulate the ColdBox application. Used mostly on advanced testing cases where you have altered the default application key.
loadColdBox
boolean
false
true
By default the base test case will load the virtual application into the application
scope so all specs can execute
unloadColdBox
boolean
false
true
The base test case will unload the virtual application from the application
scope after all specs have executed.
Parameter Name | Parameter Type | Required | Default Value | Description |
event | string |
|
| The event name to execute. e.g., |
route | string |
|
| The route to execute. e.g., |
queryString | string |
|
| Any parameters to be passed as a query string. These will be added to the Request Context for the test request. |
private | boolean |
|
| If |
prePostExempt | boolean |
|
| If |
eventArguments | struct |
|
| A collection of arguments to passthrough to the calling event handler method. |
renderResults | struct |
|
| If true, then it will try to do the normal rendering procedures and store the rendered content in the RC as |
withExceptionHandling | boolean |
|
| If true, then ColdBox will process any errors through the exception handling framework instead of just throwing the error. |
Key | Description |
| The value returned from the event handler method. This key will NOT be created if the handler does not return any data. |