tests
directory off the root of your CFWheels application, or within a subdirectory thereof.tests
directory for valid tests. Whilst you have freedom to organize your subdirectories, tests and supporting files any way you see fit, we would recommend using the directory structure below as a guide:wheels.Test
component:wheels.Test
, that component will be skipped. This lets you create and store any mock components that you might want to use with your tests and keep everything together.test
, it is ignored and skipped. This lets you create as many helper methods for your testing components as you want.var
-scope any variables used in your tests. In order for the testing framework to access the variables within the tests that you're writing, all variables need to be within the component's variables
scope. The easy way to do this is to just not var
variables within your tests, and your CFML engine will automatically assign these variables into the variables
scope of the component for you. You'll see this in the examples below.setup()
: Used to initialize or override any variables or execute any code that needs to be run before each test.teardown()
: Used to clean up any variables or execute any code that needs to be ran after each test.assert()
: This is the main method that you will be using when developing tests. To use, all you have to do is provide a quoted expression. The power of this is that ANY 'truthy' expression can be used.assert()
command as another way of using evaluate()
, it will all make sense. Remember that you can use any expression that evaluates to a boolean value, so if you can write assertions against structures, arrays, objects, you name it, you can test it!raised()
: Used when you want to test that an exception will be thrown. raised()
will raise and catch the exception and return to you the exception type (think cfcatch.type
). Just like assert()
, raised()
takes a quoted expression as its argument.Wheels.TableNotFound
error when you specify an invalid model name:debug()
: Will display its output after the test result so you can examine an expression more closely.expression
(string) - a quoted expression to display
display
(boolean) - whether or not to display the outputcfdump
attributeCollectionbeforeSave
callback that runs whenever we save a user object. Let's get started writing some tests against this model to make sure that our callback works properly./tests/models/TestUserModel.cfc
, and in the setup
function, create an instance of the model that we can use in each test that we write. We will also create a structure containing some default properties for the model.model()
method just like you would normally do in your controllers.return
in the create
action in the redirectTo()
method? The reason for this is quite simple, under the covers, when you call redirectTo()
, CFWheels is using cflocation
. As we all know, there is no way to intercept or stop a cflocation
from happening. This can cause quite a number of problems when testing out a controller because you would never be able to get back any information about the redirection.return
./tests/controllers/TestUsersController.cfc
to test that the create
action works as expected:params
that will need to be passed to the controller. We then pass the 'params' to the processRequest()
function which returns a structure containing a bunch of useful information.index
action once the action was completed.processRequest()
is only for use within the test framework.this
scope. This way it's available from outside the controller, which makes it testable.includePartial()
) outside of a request. You'll notice that if you just try and call includePartial()
from within the test suite, it won't work. Thankfully there's a fairly easy technique you can use by calling a "fake" or "dummy" controller.new.cfm
, which is the view file for the controller's new
action:processRequest()
function which will return (among other things) the generated view output.assert()
function.setup()
function so our view functions are available to the test framework.h1
tags.mixin
attribute defined in the plugin's main component.timeAgo
that extends CFWheels' timeAgoInWords
view helper by appending "ago" to the function's return value. Take note of the mixin="controller"
argument as this will play a part in how we test the plugin.tests
. We'll also need a directory to keep test assets, in this case a dummy controller that we will need to instantiate in out test's setup()
function./plugins/timeago/tests/assets/controllers/Dummy.cfc
controller contains the bare minimum for a controller./plugins/timeago/tests/TestTimeAgo.cfc
we'll need to copy the application scope so that we can change some of CFWheels' internal paths. Fear not, we'll reinstate any changes after the tests have finished executing using the teardown
function. so that if you're running your tests on your local development machine, your application will continue to function as expected after you're done testing.mixin="model"
, you will need to create and instantiate a dummy model component./tests
directory, you will also see a link to run the plugin's tests./index.cfm?controller=wheels&action=wheels&view=tests&type=app
/index.cfm?controller=wheels&action=wheels&view=tests&type=app&package=controllers
/index.cfm?controller=wheels&action=wheels&view=tests&type=app&package=controllers&test=testCaseOne
format
url parameter. Eg: format=junit
beforeAll()
- Runs once before the test suite has run. Here is where you might populate a test database or set suite-specific application variables*.afterAll()
- Runs once after the test suite has finished.packageSetup()
- Used in a test package, similar to setup()
but only runs once before a package's first test case. Here is where you might set variables that are common to all the tests in a CFC.packageTeardown()
- Used in a test package, similar to teardown()
but only runs once after a package's last test case. Here is where you might cleanup files, database rows created by test cases or revert application variables in a CFC.beforeAll()
packageSetup()
Foo.cfc - setup()
Foo.cfc - testCaseOne()
Foo.cfc - teardown()
Foo.cfc - setup()
Foo.cfc - testCaseTwo()
Foo.cfc - teardown()
Foo.cfc - packageTeardown()
packageSetup()
Bar.cfc - setup()
Bar.cfc - testCaseThree()
Bar.cfc - teardown()
Bar.cfc - packageTeardown()
afterAll()
beforeAll()
and afterAll()
, you'll need to make a few small changes to your test suite. Firstly, create a Test.cfc
in the root of your /tests/
directory. This is where you'll define your beforeAll()
and afterAll()
functions and it should look something like this:extends
attribute in your
test packages to extends="tests.Test"
. This enables the CFWheels test framework to run your functions.Test.cfc
component, we now have global setup()
and teardown()
functions will run respectively before and after every test case. If we want to prevent these from running in a particular package, we simply override the global functions like this:assert()
or grouping multiple assertions together. Whatever your requirements, there are a number of ways to use test helpers./tests/Test.cfc
.
These will be available to any package that extends this component. Be mindful of
functions you put in here, as it's easy to create naming collisions.helpers.cfm
file in any given
directory and simply include it in the package.$simplify()
reload=true
param to your url whilst developing your test packages.tests
folder of the CFWheels git repo.