Ensuring that you have unit tested all (or most) of your code in your CFWheels app is an important part of any test suite. As you hopefully know by now, the CFWheels framework features an in-built unit test framework.
The code below is a test itself, that will scan your controllers, models and views folders and make sure that you have a corresponding “test” cfc for each. If it discovers that a test is missing, it will fail.. so your app will not pass unless it has full test coverage.
To get it working, just copy the code below and save it into a file in /tests/TestsExist.cfc
(or anywhere under the tests
folder actually). You can retro-fit it to any app, though you may need to re-organise your test files somewhat.
There are some conventions that you need to follow to have it work properly, but I consider this being well organised (and you should be used to conventions by now). Your folder structure under /myapp/tests/
should look like this... Say you have a
Login.cfc
controller, your controller test cfcs themselves should be named like this TestLogin.cfc
.. but you could also have multiple test files for your Login.cfc
, just as long as the file BEGINS with “TestLogin”, eg: TestLoginRedirects.cfc
, TestLoginBusinessLogic.cfc
.. you get the idea.
The same goes for models and views. There is much one could do to improve/re-factor/customise this code, but it will hopefully catch anything you may have forgotten to test. Feel free to contribute to this project on GitHub.
So to summarise:
- Re-organise your folders as per the example above
- Re-name your test cfc files using
Test{YourControllerName}.cfc
- Copy the code into a TestsExist.cfc file under the
/tests/
folder - Run your unit tests
<cfcomponent extends="wheelsMapping.Test"> <cffunction name="setup"> <cfset testsPath = ExpandPath("tests")> <cfset controllersPath = ExpandPath("controllers")> <cfset modelsPath = ExpandPath("models")> </cffunction> <cffunction name="test_00_setup_and_teardown"> <cfset assert("true")> </cffunction> <cffunction name="test_01_all_controller_tests_exist"> <cfdirectory action="list" directory="#controllersPath#" filter="*.cfc" name="controllers" /> <cfdirectory action="list" directory="#testsPath & '/' & 'controllers'#" filter="*.cfc" name="controllerTests" /> <cfset assert("controllerTests.recordCount gt 0")> <cfloop query="controllers"> <cfif ListFindNoCase("Controller.cfc,Wheels.cfc", controllers.name) eq 0> <cfset abbrev = ListFirst(controllers.name, ".")> <cfset isTestFileFound = false> <cfloop query="controllerTests"> <cfif FindNoCase("Test" & abbrev, controllerTests.name) gt 0> <cfset isTestFileFound = true> <cfbreak> </cfif> </cfloop> <cfset assert("isTestFileFound", "controllers.name")> </cfif> </cfloop> </cffunction> <cffunction name="test_02_all_model_tests_exist"> <cfdirectory action="list" directory="#modelsPath#" filter="*.cfc" name="models" /> <cfdirectory action="list" directory="#testsPath & '/' & 'models'#" filter="*.cfc" name="modelTests" /> <cfset assert("modelTests.recordCount gt 0")> <cfloop query="models"> <cfif ListFindNoCase("Model.cfc,Wheels.cfc", models.name) eq 0> <cfset abbrev = ListFirst(models.name, ".")> <cfset isTestFileFound = false> <cfloop query="modelTests"> <cfif FindNoCase("Test" & abbrev, modelTests.name) gt 0> <cfset isTestFileFound = true> <cfbreak> </cfif> </cfloop> <cfset assert("isTestFileFound", "models.name")> </cfif> </cfloop> </cffunction> <cffunction name="test_03_all_view_tests_exist"> <cfdirectory action="list" directory="#controllersPath#" filter="*.cfc" name="controllers" /> <cfdirectory action="list" directory="#testsPath & '/' & 'views'#" filter="*.cfm" name="viewTests" /> <cfset assert("viewTests.recordCount gt 0")> <cfloop query="controllers"> <cfif ListFindNoCase("Controller.cfc,Wheels.cfc", controllers.name) eq 0> <cfset abbrev = ListFirst(controllers.name, ".")> <cfset isTestFileFound = false> <cfloop query="viewTests"> <cfif FindNoCase("Test" & abbrev, viewTests.name) gt 0> <cfset isTestFileFound = true> <cfbreak> </cfif> </cfloop> <cfset assert("isTestFileFound", "controllers.name")> </cfif> </cfloop> </cffunction> <cffunction name="teardown"> </cffunction> </cfcomponent>
you da’man! Thanks!