coldbox developer training – session 4
TRANSCRIPT
Coldbox Developer TrainingSession 4
Bill BerzinskasUNC Chapel Hill
Office of Research Information Systems
Questions from Session 3?
Overview
The ModelWireboxKickstarting Model DevelopmentUnit Testing Model CFCs
The Model - Reloaded
The Model layer represents your data structures and business logic. The domain-specific representation of the information that the application operates on.
A service layer approach is a way to architect applications in which there is a layer that acts as a service or mediator to your domain models, data layers and so forth. This layer is the one that event handlers can talk to in order to interact with the domain model.
The Model - Reloaded
Last week, we looked at a data diagram for our application and determined that we’re mostly focused on “recommendation” table
We will create a “recommendationService” and a “recommendationGateway” (and later a “programGateway”
The Model - Reloaded
We also determined we will need to perform SELECT / SAVE / DELETE functionality against this table
We can use “DataMgr” in out gateways stub this out quickly, simply extend /common/model/basegateway
<cffunction name=“get”>◦ <cfargument name=“id”>◦ <cfreturn super.get(“recommendation”, arguments)></cffunction>
<cffunction name=“save”>◦ <cfargument name=“data”>◦ <cfreturn super.save(“recommendation”, arguments.data)></cffunction>
Wirebox
Wirebox allows us to quickly “inject” dependencies into CFC’s – generally with a simple CFPROPERTY tag
◦<cfcomponent> <cfproperty name=“myGateway” inject=“myGateway”>
◦ INSTEAD OF <cfcomponent>
◦<cffunction name=“init”> <cfset myService=createObject(‘component’, ‘model.
myGateway’).init(datasource=….)>
Wirebox
Wirebox also gives us access to many more objects via its “DSL” (domain specific language)
◦<cfproperty name=“myDSN” inject=“coldbox:datasource:main”>
◦<cfproperty name=“logBox” inject=“logbox”>
◦<cfproperty name=“HeaderText” inject=“coldbox:setting:headerText”>
Wirebox
Wirebox is also an all-or-nothing approach. If you start intermingling auto-wire and non, you’re probably asking for trouble
Why bother?! Wirebox allows the dependencies to be easily visible for a given CFC and more importantly, when unit testing, it allows us to “swap out” objects (mocks, test objects, etc)
Model / Wirebox Questions?
MXUnit + Testing Model CFCs
UNIT TESTING◦In computer programming, unit testing is a
procedure used to validate that individual units of source code are working properly. A unit is the smallest testable part of an application. In procedural programming a unit may be an individual program, function, procedure etc, while in object-oriented programming, the smallest unit is always a Class; which may be a base/super class, abstract class or derived/child class. Wikipedia
MXUnit + Testing Model CFCs
It might be tedious and take time to get into the flow of 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
MXUnit + Testing Model CFCs
Functional testing relies on the action of verification of specific requirements in our software. This can usually be tracked via use cases or Agile stories. "Can a user log in?", "Does the logout work", "Can I retrieve my password", "Can I purchase a book?". Our main focus of functional testing is that all these use cases need to be met.
MXUnit + Testing Model CFCs
Non Functional Testing are testing aspects that are not related to our specific requirements but more of the quality of our software.
◦ Can our software scale?
◦ Is our software performant?
◦ Can it sustain load? What is the maximum load it can sustain?
◦ What do we do to extend our security? Are our servers secure?
MXUnit + Testing Model CFCs
Bugs Cost Money!!
MXUnit + Testing Model CFCs
Static testing relies on human interaction not only with our code but with our developers and stakeholders. Some aspects can be code reviews, ui walkthroughs, design and modeling sessions, etc.
Through experience, we can also attest that static testing is sometimes omitted or considered to be a waste of time. Especially when you need to hold up on production releases to review somebody else’s work.
Not only that, but sometimes code reviews can spark animosity between developers and architects as most of the time the idea is to find holes in code, so careful tact must be in place and also maturity across development environments. In true fun we can say: “Come on, take the criticism, it is for the common good!”
MXUnit + Testing Model CFCs
On the other side of the spectrum, dynamic testing relies on the fact of executing test cases to verify software. Most of the time it can be the developers themselves or a QA team than can direct testing cases on executed code.
Unit Testing - We should be testing all the CFCs we create in our applications. Especially when dealing with Object Oriented systems, we need to have mocking and stubbing capabilities (See MockBox).
Integration Testing - Unit tests can only expose issues at the CFC level, but what happens when we put everything together? Integration testing in ColdBox allows you to test requests just like if they where executed from the browser. So all your application loads, interceptions, caching, dependency injection, ORM, database, etc.
UI Integration Testing - Testing verification via the UI using tools like Selenium is another great idea!
Load Testing - There are tons of free load testing tools and they are easy to test how our code behaves under load. Don't open a browser with 5 tabs on it and start hitting enter! We have been there and it don't work that great :)
MXUnit + Testing Model CFCs
Testing Vocabulary◦ Test Plan: A test plan documents the strategy that will be used to verify and
ensures that a product or system meets its design specifications and other requirements.
◦ Test Case: A set of conditions or variables under which a tester will determine whether an application or software system is working correctly or not. In our ColdFusion space, it might be represented by an MXUnit test case CFC.
◦ Test Script: A set of instructions that will be performed on the system under test to test that the system functions as expected. This can be manual or automated. Sometimes they can be called runners.
◦ Test Suite: A collection of test cases that are intended to be used to test a software program to show that it has some specified set of behaviors or met expectations.
◦ Test Data: A collection of pre-defined data or mocked data used to run against our test cases
MXUnit + Testing Model CFCs
Testing Vocabulary◦ Test Harness: A collection of software and test data configured to test a
program unit by running it under varying conditions and monitoring its behavior and outputs.
◦ TDD: Test Driven Development - A software development process that relies on the repetition of a very short development cycle: first the developer writes a failing automated test case that defines a desired improvement or new function, then produces code to pass that test and finally refactors the new code to acceptable standards.
◦ Mock Object: Simulated objects that mimic the behavior of real objects in controlled ways
◦ Stub Object: Simulated objects that have no behavior or implementations and can be controlled and injected with functionality
◦ Assertions: The function to test if a predicate is true or false: X>1, 4=5
MXUnit + Testing Model CFCs
We’ve seen MXUnit a LITTLE bit, but we’ve not begun to scratch the surface!
Coldbox offers us base unit tests which make “setup” easier, and also add an enormous amount of functionality (mocking, stubbing, querySim, etc)
The MXUnit test runner in CFBuilder allows us to develop / test without leaving the IDE!
MXUnit + Testing Model CFCs
We’re going to begin by looking at “baseModelTest”
Basic Setupcomponent extends="coldbox.system.testing.BaseModelTest" model="myApp.model.User"{
function setup(){ super.setup(); user = model;
}
function testisActive(){ isActive = user.isActive();Debug(isActive);assertEquals( false, isActive);
}
}
Questions?
Assignment
Create a unit test CFCs◦ app/test/unit/recommendation/recommendationGatewayTest.cfc
<cfcomponent name=“recommendationGatewayTest“ extends="coldbox.system.testing.BaseModelTest"model="ftf_bb.model.recommendation.recommendationGateway">
<cffunction name="setUp"><cfscript>
super.setup();datasource=getMockDatasource(name='fixed_term_faculty');model.$property(propertyName='datasource', mock=datasource);this.myComp = model.init(datasource = datasource);session.pid = '710917825';
</cfscript></cffunction>
</cfcomponent>
Next, add a test for “get”
<cffunction name=“testGet”><cfscript>
Results=this.myComp.get();assertTrue(isQuery(results));
</cfscript></cffunction>
Assignment
Running this url in your browser should show a failed test for testGet◦ test/unit/recommendation/recommendationGatewayTest.cfc?
method=runtestremote&output=html
Create an accompanying model CFC◦ App/model/recommendation/recommendationGateway.cfc
Assignment
And an accompanying model CFC◦ App/model/recommendation/recommendationGateway.cfc
<cfcomponent name=“recommendationGateway” extends=“/common/model/baseGateway_Autowire”>
◦ </cfcomponent>
Run test again – different error! This is because we’re running the “abstract” get method from baseGateway – lets define a concrete “get”◦ <cffunction name=“get”>
<cfreturn super.get(“recommendation”)></cffunction>
Run test agan – SUCCESS! We just created our first testable model function! Furthermore, we’re selecting records from the db without any sql!
Assignment
Lets create a save test
<cffunction name=“testSave”><cfscript>
Data.fname=“test”;Data.lname=“user”;Data.pid=“123456”;Results = This.mycomp.save(data);assertTrue(isNumeric(results));
</cfscript></cffunction>
Run tests – FAIL!
Assignment
Lets create a save method
<cffunction name=“save”><cfargument name=“data”><cfscript>
return super.save(“recommendation”, arguments.data);</cfscript>
</cffunction>
Run tests – PASS!
Assignment
Lets create a delete test
<cffunction name=“testDelete”><cfscript>
This.mycomp.delete(id=1);assertTrue(true);
</cfscript></cffunction>
Run tests – FAIL!
Assignment
Lets create a delete method
<cffunction name=“delete”><cfargument name=“id”><cfscript>
return super.delete(“recommendation”, arguments);</cfscript>
</cffunction>
Run tests – PASS!
SHAZAM!
We’ve just created a gateway CFC to handle very basic CRUD operations and associated unit tests with less then 60 lines of code!
Next week, we’ll create a service and start consuming this gateway in our recommendation.submission handler