Evils of Global Variables when Unit Testing
Let's jump right to some code.
<cfargument name="email">
<cfset var getsomething="">
<cfquery name="getsomething" datasource="mydsn">
select *
from sometable
where email='#arguments.email#'
</cfquery>
<cfreturn getsomething/>
</cffunction>
versus:
<cfset var getsomething="">
<cfquery name="getsomething" datasource="mydsn">
select *
from sometable
where email='#session.user.getEmail()#'
</cfquery>
<cfreturn getsomething/>
</cffunction>
They look pretty similar right? Near identical. Neither is really easier or harder to read. Performance wise, I suspect you couldn't see much of a difference. If you're thinking to yourself, "Aww geez Steve Nelson is about to go on a week long rampage about something." You would be right!!
Here's the problem that I see and it's pretty simple. Unit Testing.
The first one is dead simple to create a unit test for. You cfinvoke the method, pass in an email. Boom! You're done. Hell you could write a bunch of different unit tests in a matter of minutes to try out different types of email addresses. Maybe throw in some Japanese kanji just to see what happens. Maybe do a SQL injection test since I didn't use cfqueryparam in my example. Then setup your testing process to continually test them with a test harneness runner.
The second function is dramatically more difficult to unit test. You can try the same thing, cfinvoke the method (no arguments). Damn. Error. It doesn't know what session.user is. So now you have to create another CFC. (Which you were going to do anyway right? I guess we need to unit test that one too) Create the CFC with at least one method getEmail. Great. Run the first test it works. Beautiful. But now let's try our Japanese kanji test. How do we change that value returned by getEmail? We have two choices. The obvious choice is to edit the original getEmail method and have it return the static Japanese kanji string. Fine. But now you're no longer testing the english string. So the better choice is to create ANOTHER method called updateEmail which modifies the email address returned. Then the second unit test has to call the updateEmail() before it calls getEmail(). Great it works! Now run the first test again... aw damn it! It's still displaying the kanji. What the heck? Oh right, the first one ALSO has to call the updateEmail() method because it's a session scope variable. The Session scope is a persistent global variable. One unit test affected another unit test when global variables are used.
Did I lose you? Good. I was trying. Chew on that a bit. I'll come back to some unit testing code soon. Personally I prefer passing in every argument my method needs.
-Steve Nelson
By the way, if you're looking for CF Architects or simply some extra CF hands, send us an email or give us a call at +1-970-223-2278. We're about to finish up a few projects and will have some free time shortly.

