Blog

Blog Archives - 19 Record(s)

Year: '2014' - Month: '1'

18
January 2014

Gavin Pickin

Git for Dummies - Rolling back a Commit - Power of Git

Source Control

Welcome back to our Git for Dummies, or introduction to Git series. Its been a while since my last post on this, but today I had to write one. One of the reasons we should really use source control was made very evident today, so I decided to share my story, albeit, at my own expense, you'll find out why, soon. So I got a text message today, asking if I could restore a folder on one of our servers. Some how, the folder got zapped, not sure by who, not sure how, but we need to restore it, so it can be business as usual. Should we restore from backup? We could, but we have Git setup for this project, so we can check the commits, and restore the folder from the last good commit, simple enough... so lets go through it.

Now, first, I wanted to know when did this folder suddenly disappear. You can use command line for all of this stuff, and we have been so far in this dummies series, but sometimes, there is a time and a place for GUI, and using BitBucket (this is where we host our private git repos) and BitBucket's website is a perfect example for this, instead of looking through command line readout.

So I login to BitBucket.org, pull up the repo in question, and look at the Commits page. I can see which author committed, and when, and click on each one, to see where the FOLDER was mentioned as missing. I find the commit, and then look at the date and time, and I think to myself, oh snap. Days like this, you need Git to help you out of a mess, that Git got you into. Well, not Git per say, but cleaning up the Git repo, to remove files and folders we don't need, late at night, and you accidentally remove the entire folder, not just the deleted file you intended, and being too tired to notice the difference. Yes, it was me, I removed the whole folder from my repo... and committed the change, eliminating that folder.

This is Git though, and no matter how it happened, we have ways to Clone a repo, switch to a previous commit etc, so that is exactly what I did, and it didn't take long, much faster than restoring from a backup, wherever those are.

To roll back your repo to a given commit, you need to know the commit id. We know the notes on it, by looking at the BitBucket.org commit screen, but the IDs there are not the same as on the machine you made the commit on. So at the command line, in the repo, you can type 

$ git log

 

This will dump the whole log, and it give you something like this.

 

This is a pretty nice format, but the log function is pretty powerful, you can just ask for one liners

$ git log --pretty=oneline
94231de7ceceb89a6fe3aaf1842ca29a5999e85e Fixed Git Ignore
5c87e4758c6aa7116853bf1fd81666ed69c44816 GitIgnore Cleanup on Production
5270d58e60db2eee8b7d50fbf67606555da7b5a3 Updated for Dev Environment Variables
3016eadbdaf12f5be4f0179a1951420baa1ee99c Upload Letter Product ID Fixed
e930333e9556d6ee5dada58c935682078319501a Production Updates - Git Ignores
b5d78bf6c0535da1559e260d30053543c4c50454 First set of files from New Supernova

 

And you can even roll your own style if you want.

$ git log --pretty=format:"%H - %an: %s"
94231de7ceceb89a6fe3aaf1842ca29a5999e85e - Gavin Pickin: Fixed Git Ignore
5c87e4758c6aa7116853bf1fd81666ed69c44816 - Gavin Pickin: GitIgnore Cleanup on Production
5270d58e60db2eee8b7d50fbf67606555da7b5a3 - Gavin: Updated for Dev Environment Variables
3016eadbdaf12f5be4f0179a1951420baa1ee99c - Gavin - Supernova Production: Upload Letter Product ID Fixed
e930333e9556d6ee5dada58c935682078319501a - Gavin - Supernova Production: Production Updates - Git Ignores
b5d78bf6c0535da1559e260d30053543c4c50454 - root: First set of files from New Supernova


You can limit the time limit on the log too by doing something like this

$ git log --since=1.weeks
commit 94231de7ceceb89a6fe3aaf1842ca29a5999e85e
Author: Gavin Pickin <gavin@netxn.com>
Date:   Sat Jan 18 12:34:11 2014 -0800

    Fixed Git Ignore

commit 5c87e4758c6aa7116853bf1fd81666ed69c44816
Author: Gavin Pickin <gavin@netxn.com>
Date:   Thu Jan 16 14:46:10 2014 -0800

    GitIgnore Cleanup on Production

 

You can find a lot of cool info on the Commit History at this link http://git-scm.com/book/en/Git-Basics-Viewing-the-Commit-History
The git-scm.com site has a lot of good information.

Ok, so now we have found the right commit hash (the one before I screwed up royally)... we need to tell Git to roll back our repo to that commit.

$ git reset --hard 5c87e4758c6aa7116853bf1fd81666ed69c44816
HEAD is now at 5c87e47 GitIgnore Cleanup on Production

 

Pretty simple looking, git reset and the hash. What does the --hard do? The --hard tells git we want to roll back the commit history, and the files. Without --hard, it rolls back the commit history, but leaves the file structure in tack. We need the --hard, and you can see the files are back. I look, and ta-daa!!!, the folder is back.

Now the local repo is fixed, what about the remote repo? Lets do a git push.

$ git push
To ssh://git@bitbucket.org/xxxxx/www.xxxxx.com.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'ssh://git@bitbucket.org/xxxxxx/www.xxxxx.com.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

 

This is a nice feature, making sure someone doesn't roll back the remote repo by mistake if they're playing with rolling back a commit on their local repo. To push and force the remote to roll back, we need to add a -f flag.

$ git push origin master -f
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (6/6), 1.14 KiB, done.
Total 6 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/xxxxxx/www.xxxxxx.com.git
 + 354a3d4...94231de master -> master (forced update)

 

If we login to BitBucket.org again, look at commits, and we'll see the roll back was a success, and now the remote and the local are in sync, with the folder back in play. Saved my bacon, glad I got to play with the commit rollbacks, but of course, I would have preferred more practice without needing it.

As usual, I want to add that you should play, test, and get used to using all of Git's functions, rolling back, locally and remotely, and really get familiar before making a mess of things, like me :) Git's a great tool, but like any great tool, doesn't mean you'll do great things, because the operator is in control (or is supposed to be). So practice, make sure you know what its actually doing. Setup a git101 repo and play with it. See what happens when you do this, and that. It was interesting watching what happens when you use branches... which is probably going to be the next entry in this series.

Thanks for reading,

Gavin

17
January 2014

Gavin Pickin

Unit Testing 06 - Mocking External Dependencies Part 2

Dependency Injection, Unit Testing

Welcome back to the 6th post in our Unit Testing for Dummies Series, where I walk you through building a small ColdFusion (cfml) application using Test Driven Development, running our tests with TestBox, Ortus Solutions and ColdBox's testing platform. 

Last time we created our WebsiteDAO.cfc and a simple method getWebsite() that would return a query containing the Website Details. In our method, we returned only an empty query at this time, since we are still fleshing out our App, as we explore through our Test Driven Development. Today, we're going to use Mocks to isolate our CUT component under test, which is our Website.cfc (website service). 

Step 1 - Mock our WebsiteDAO.cfc

Lets look at out testGetWebsiteSuccess() test method.

Inside blog06/Step1/test/unit/Website.cfc

function testGetWebsiteSuccess() {

    $assert.typeOf( "query", website.getWebsite(4), "Query Expected - Not Received" );
    $assert.assert( Website.getWebsite(4).recordcount >= 1, "Query with 1 or more Records expected");
}

 

Our setup() function currently creates a new Website object, and sets the DAO each and every time.

function setup() {

    variables.Website = new blog06.Step1.model.Website().init();
    variables.Website.setDAO( variables.WebsiteDAO );
}

 

We pull the WebsiteDAO from the beforeTests() method. This creates it one time, and then is used in each and every setup() call before every test.

function beforeTests() {

    variables.mockbox = new testbox.system.testing.Mockbox();
    variables.WebsiteDAO = new blog06.Step1.model.WebsiteDAO().init();
}

 

We really want our DAO to be reset each and every test too, especially if we're going to be mocking everything in it, so lets move some code around.

Take out the DAO instantiation in beforeTests()

    variables.WebsiteDAO = new blog06.Step1.model.WebsiteDAO().init();

 

Instead of moving that into the setup() method, lets just create a new empty mock object, and we'll mock everything in the DAO. We are adding a argument called ClearMethods, which removes all the methods in the object, so everything has to be mocked. This ensures you don't forget to mock one, and use the actual original methods. 

function setup() {

    variables.Website = new blog06.Step1.model.Website().init();
    variables.WebsiteDAO = variables.mockbox.createMock(className='blog06.Step1.model.WebsiteDAO', clearMethods=true);
    variables.Website.setDAO( variables.WebsiteDAO );
}

 

Note, you could just use createStub() if you want a completely empty Mock object… but if you do that, in your errors, the component name will be testbox.system.testing.mockutils.Stub instead of blog06.Step1.model.WebsiteDAO. If you have multiple cfcs you are stubbing, it might get confusing all being of the same component type, but its powerful. In fact, I have an idea about stubs, and I might have to write an article about this another day.

So lets run our test, and we get an error… 

component [blog06.Step1.model.WebsiteDAO] has no function with name [getWebsite]

This means we successfully mocked our DAO. 

Step 2- Mock our getWebsite() method in our Mocked WebsiteDAO

We want to mock the getWebsite() method. We have 2 assertions so far if you look in our testGetWebsiteSuccess() method.

Inside blog06/Step2/test/unit/Website.cfc

function testGetWebsiteSuccess() {

    $assert.typeOf( "query", website.getWebsite(4), "Query Expected - Not Received" );
    $assert.assert( Website.getWebsite(4).recordcount >= 1, "Query with 1 or more Records expected");
}

 

To mock a method, we use the $() method, and pass in the method name, and we can pass in a result right there too.

variables.WebsiteDAO.$(method="getWebsite",returns=queryNew(""));

 

Now, since we're going to call this 2 times, we can just have it return the same result every time using this… so for now, this will suffice. 

Lets use Query Sim to simulate a query, and return that.

function testGetWebsiteSuccess() {

    var mockQuery = mockBox.querySim("id, name
        1|www.gpickin.com
        2|www.gavinpickin.com");

    variables.WebsiteDAO.$(method="getWebsite",returns=mockQuery);

    $assert.typeOf( "query", website.getWebsite(4), "Query Expected - Not Received" );
    $assert.assert( Website.getWebsite(4).recordcount >= 1, "Query with 1 or more Records expected");
}

 

Query Sim is a cool little query shorthand tool, set the column names, line breaks for new records, and pipes for separators.
This makes a query, with 2 rows, and returns it every time, so both assertions pass.

Step 3 - Create testGetWebsiteNotFound() test and mock the results

Lets create a new method, called testGetWebsiteNotFound() and we'll use a slight variation on the assertions, and on how we mock the methods. First, lets look at the assertions.

function testGetWebsiteNotFound() {

    $assert.isEqual( 1, website.getWebsite(2).recordcount);
    $assert.isEqual( 0, Website.getWebsite(4).recordcount);
}

 

In this case, we're asserting that the return value matches what we expect. We're looking at our mocked method getWebsite() and expecting when we pass the number 2 in, we'll get 1 record. When we pass in the number 4 in, we'll get 0 records.

So, lets mock it with like our previous test.

var mockQuery = mockBox.querySim("id, name
    1|www.gpickin.com
    2|www.gavinpickin.com");
variables.WebsiteDAO.$(method="getWebsite",returns=mockQuery);

 

This will return 2, both times. Lets run it and see.

As you can see, it states Expected [1] but received [2]

So, how can we make it successfully pass both assertions?

We can set our mock to pass different results… which is really cool… and its easy to do too. Mockbox manages the count, and just rotates through your list of results if you use the $results() method.

So we can do this.

function testGetWebsiteNotFound() {

    var mockQuery1 = mockBox.querySim("id, name");
    var mockQuery2 = mockBox.querySim("id, name
        1|www.gpickin.com");

    variables.WebsiteDAO.$(method="getWebsite").$results(mockQuery1,mockQuery2);

    $assert.isEqual( 1, website.getWebsite(2).recordcount);
    $assert.isEqual( 0, Website.getWebsite(4).recordcount);
}

 

Now the test fails because query1 has 0 records, and query2 has 1 record count, but we have them backwards.
Lets change the order of the returning results.

function testGetWebsiteNotFound() {

    var mockQuery1 = mockBox.querySim("id, name");
    var mockQuery2 = mockBox.querySim("id, name
        1|www.gpickin.com");

    variables.WebsiteDAO.$(method="getWebsite").$results(mockQuery2,mockQuery);

    $assert.isEqual( 1, website.getWebsite(2).recordcount);
    $assert.isEqual( 0, Website.getWebsite(4).recordcount);
}

 

Now the test passes.

The issue with this is, you have to remember the order, so wouldn't it be nice if you could return different results, based on the input? Well, of course you can.

Lets update our tests to look like this.

function testGetWebsiteNotFound() {

    var mockQuery1 = mockBox.querySim("id, name");
    var mockQuery2 = mockBox.querySim("id, name
        1|www.gpickin.com");

    //variables.WebsiteDAO.$(method="getWebsite").$results(mockQuery2,mockQuery1);
    variables.WebsiteDAO.$(method="getWebsite").$args(2).$results(mockQuery2);
    variables.WebsiteDAO.$(method="getWebsite").$args(4).$results(mockQuery1);

    $assert.isEqual( 1, website.getWebsite(2).recordcount);
    $assert.isEqual( 0, Website.getWebsite(4).recordcount);
}

 

Now, the order can be anything it wants to be, when these arguments are passed in, it returns the given results.Note, if you're not catching that argument, it will give you an error.

So we've changed our method a few times, which shows you there are many ways to skin a cat, you just need to decide whats best for you, and your project.

Thanks again for following along,

Gavin

16
January 2014

Gavin Pickin

TGIF - Take a little time for yourselves - Client Free Friday

Chit Chat, Client Free Friday

In a business like ours, we're almost always putting out fires, there are always deadlines, and new priorities, and when you have a couple hundred clients, there is always something to be done for someone. Thats pretty good, usually, except, its really hard to push yourself, and work on your own website, brand, marketing and social media, when you are so busy with everyone else's. 

At the start of this year, I decided to put into action, an idea that emulates some of the well known tech companies, and declare Fridays a "Client Free Friday." 

As you can tell from this weeks lack of blog posts, this week has been hectic, and I haven't had time to get even my own personal items done this week... but we had a couple of big launches, and the big big launch was really successful, the other one, running into issues with IE 11 and compatibility mode, but life goes on. So, especially on weeks like this, this Client Free Friday is a way to make sure we look after our own house, and not always looking after everyone else's.

What exactly does this mean? 
This means we are going to try and transition ourselves into a work schedule where Monday to Friday, we work on Client Projects and tasks, like normal. Friday is going to be different, we're going to spend Friday learning, working as a team, enhancing ourselves, or one of our Products (which part of the process will be identify, prioritize and focus), and try to really push ourselves to be better people, a better team, and a better company.

Of course, there will be "emergency" or "urgent" work that will be attended to, phone calls, email support, and our usual array or support capabilities, but wherever possible, we will put the work off until Monday, where we can dedicate all of our energy to it.

So what will these Fridays include? 
To be honest, I want to spend as much time in the Conference Room, with a couple of 40 inch monitors, and a 70 inch whiteboard, and get ideas moving and flowing. I want to see plans, designs, mockups, training, sharing, paired programming (angular and cfml), and some good socializing while we do it. 

Due to the holidays, and everything else, this is the first real Client Free Friday (this is what it is called until I find a better name lol).

So what is the plan for tomorrow?

  • We are going to work on our server migration. There are a lot of sites still not tested fully on ColdFusion and Railo on our new Centos Linux Server environment. We're going to identify a few methods for testing these sites, and appropriately report the bugs so we can systematically resolve those issues.
  • Discuss and Flesh out our Multi-Server Website Management Tool. Not all of our sites on all of our servers are managed through a central system, we're going to plan our system, so we can ensure all sites are put into the new system as we complete our migration.
  • Ensure all sites in the migration queue are Git'ed and the latest version is added to our Remote Repository.
  • Discuss our possible Brand Name Change, are we going to pull the trigger, yes / no, and if so, how are we going to market this brand, complete takeover, a sister brand, hip cool spin off.

This doesn't look like a long list... but for our small team, I think this will be a major under-taking. 
I look forward to tackling these items and making headway on these tasks. I wish more days were Fridays actually, but you can't always get what you want.

Does your office dedicate a day of the week, or a week in the month for their own projects, or do you treat your company project like a normal project and manage it the same way? I'd appreciate the feedback. 

Enjoy your Friday however you spend it.

Gavin

10
January 2014

Gavin Pickin

Conferences - New Conference - Into the Box - Day Before cf.Objective() - May 13, 2014

cfObjective, ColdBox, Conferences

It's Coming! The first ever Box focused conference.
May 13th, 2014 - 1 day before cf.Objective() 

Yes, thats right, cf.Objective() just got a little better, because the day before cf.Objective(), Ortus Solutions, makers of ColdBox, ContentBox and everything *Box are hosting their own 1 day Conference with speakers from around the world... called Into the Box.

The speakers and schedule are not 100% finalized just yet, but there will be a couple of bigger community members speaking, as well as all of the Team ColdBox people... and maybe yours truly, but I'm not sure if thats a selling point or not :) 

With the release of TestBox, it looks like Testing will have a strong showing, as it seems to be a hot topic right now... but you will have the opportunity to get introduced to all of the ColdBox related Box products, as well as some more advanced topics too for those already familiar with the Box family. There will be 2 tracks, so there will be options for content, and plenty of time to start the networking early.

The venue for this event is the Homewood Suites by Hilton, right across the road from the Radison Blu at the Mall of America. 

Homewood Suites by Hilton Minneapolis-Mall Of America
2261 Killebrew Drive 
Bloomington, MN 55425

FYI - Right next to the Homewood Suites is the Country Inn and Suites, which is a nice Hotel, and a good deal cheaper than the Radison Blu. If you want to some on accommodation, and you can get to cf.Objective() a couple of days early, and say in the Country Inn and Suites for both Conferences, if you wanted to.

You can sign up for announcements at their temporary website here at http://www.intothebox.org/. Their full website is almost ready, and when that is announced you will get all the information you need. Until then, follow them on twitter @GoIntoTheBox.
If you are already sold, you can go online today and pick up your tickets from EventBrite here 

Last year I attended a 3 day Pre-Conference Training and made a week out of cf.Objective(), and this year is looking great again with Into the Box to get things kicked off. I hope to see you all there, make this a week of learning and networking.

See you there,

Gavin

09
January 2014

Gavin Pickin

Unit Testing 05 - External Dependencies and Dependency Injection

Dependency Injection, Unit Testing

Our last post was an introduction into what is Mocking, and why we Mock in Unit Testing. We looked at Mocking an dependency of one function on another function internal to our CUT component under test. In this post, we're going to look at dependencies of our function on other objects and functions, and how we use Dependency Injection, or Inversion of Control to give our Website service object access to our WebsiteDAO.

Step 1 - Create testGetWebsiteSuccess() test

We're going to create a test that called website.getWebsite() passing in an ID, and we expect to get a query back. We are continuing in our TDD, creating the test first, then we'll create the method in our component.

function testGetWebsiteSuccess() {
    $assert.typeOf( "query", website.getWebsite(4), "Query Expected - Not Received" );
}

 

Simple test, we pass in the id of 4. We expect a query.
Run the test, and we see our predicted fail.

Now, lets create our getWebsite() method in our component.
blog05/step1/model/Website.cfc

function getWebsite( websiteid ) {
    var q = "";
    return q;
}

 

We create a simple method, creating a variable q to store our query in it, and then returning q to our test.
Lets run our test, and see what we get.

Query Expected - Not Received? so now we have to write our function to return a query. 

 

Step 2 - Create WebsiteDAO.cfc and Introduction to Dependency Injection

This component Website.cfc is practically our Website Service, so we're going to remove the DAO from it, and we're going to create WebsiteDAO.cfc and call that from our Website (service) to get the data, from wherever we decide to get it from. Using a DAO, we can hide the implementation from our Website (service) and thats what we are testing, so lets get moving.

Create a file
blog05/step2/model/WebsiteDAO.cfc

component displayname="Website DAO" hint="I look after all DAO for the Website Service"  output="false"
{
    function init() { return this; }
}

 

Now we have a DAO, we want our Website service to call the DAO to get the Website query. Lets just create a placeholder for right now, because we haven't decided how we want to implement it just yet, so we'll just add a simple function.

function getWebsite( websiteid ) {
    var q = queryNew("");
    return q;
}

 

This might look familiar, since it is almost the same as our service function is at the moment? except, we're creating a new empty query to pass back. It's not complete, but its a start, and its enough to start testing, so lets get back to our Website service, and update our function to call our DAO for the query it needs.

Assuming we have access to an object called WebsiteDAO inside of our Website service, we'll update our function to look like this.

function getWebsite( websiteid ) { 
    var q = "";
    q = WebsiteDAO.getWebsite( websiteid );
    return q;
}

 

Note: Of course I could remove the var declaration, and return the query directly, but I'm trying to make sure its clear, and you can see how we are building on top of our previous code.

Ok, great, our function looks good, our service gets the Website query, and returns it. Now if we run our tests, we'll get an error because the WebsiteDAO object is not available.

Now, how do we make initialize WebsiteDAO so our Website service can use it? This is something you have probably dealt with a thousand times before, but its not obvious to you, unless you know? so I'll go back to the basics. When you create an object, ColdFusion spins one up for you, and thats great. We want a Website service, so CF spins one up, but inside our Website service, we want a WebsiteDAO, so CF can spin one up, so now we have a Website service, with a WebsiteDAO service inside of it. This is not too bad, until, you think about objects that might need 4 or 5 other objects, and those objects need objects, and if each object makes an instance of those objects within itself, which in tun creates more instances, your couple of objects can get really big and have a nested mess of instances. 

The other big reason that this is not ideal, is coupling. Coupling is the term deferring to how much an object relies upon another. For object A to use B, A must know how to call B, what it expects, and what it returns? but if A needs to know how to create B, what B needs to be created, requires A to know too much about B. If several objects use B, like C, D, E and F, and they all need to create B to use it, then they all need to know how to create B as well. This is too much dependency, too high of coupling?  A should have a contract with B, or an API, methods, arguments and returns, but A shouldn't need to know anything more. This is one of the keys of Object Oriented Programming. 
This is what is known as the Object Taking What they Need. The Control is with the Object.

The other approach to this, commonly known as Inversion of Control, takes the control away from the Object, instead of the Object taking what it needs, you give the object what it needs. Another name for this, is Dependency Injection? and it sounds a lot more complicated than it actually is. If an object needs something, instead of the object asking for one, you just give it one. So instead of our Website serve making its own DAO, what we do is create one, and INJECT it into our Website service? this way, we can have a few objects, but the objects have the knowledge of where the other objects are, so they can use them when they want to.

I said its not that complicated, so how do we do it?

We create the WebsiteDAO instance, in a variable, like you normally would, and you create the Website service like you normally would? and then you inject the WebsiteDAO into the Website service, by passing a reference into the Website service? like so (adding onto our beforeTests() method)

blog05/step2/test/unit/Website.cfc

variables.mockbox = new testbox.system.testing.Mockbox();
variables.Website = new blog05.Step2.model.Website().init();
variables.WebsiteDAO = new blog05.Step2.model.WebsiteDAO().init();
variables.Website.setDAO( variables.WebsiteDAO );

 

Our MockBox definition remains the same, as does our variables.Website init. We init the DAO the same was as we did the variables.Website, but after the variables.Website and variables.WebsiteDAO are initialized, then we pass the variables.WebsiteDAO into the setDAO() method of the variables.Website component. We are injecting the reference into the object, so not the Website service knows about the WebsiteDAO service. 

blog05/step2/model/Website.cfc - Add new function

function setDAO( DAO) {
   variables.WebsiteDAO = DAO;
}

 

Now, if you remember, our last post, we also created a new version of variables.Website after we mocked some functions, so we reset it. This might get tricky to remember when we need a fresh Website service, and to make sure we set the DAO each time as well, so lets take out the init of the Website service from beforeTests() and put it in the setup() so it runs before each and every test. MockBox and WebsiteDAO never change, so we can init them just once.

// executes before all tests
function beforeTests() {
    variables.mockbox = new testbox.system.testing.Mockbox();
    variables.WebsiteDAO = new blog05.Step2.model.WebsiteDAO().init();
}

function setup() {
    variables.Website = new blog05.Step2.model.Website().init();
    variables.Website.setDAO( variables.WebsiteDAO );
}

 

If you are using ColdBox or FW/1, you can use their DI tools, WireBox for ColdBox and DI/1 for FW/1. A lot of ColdFusion projects still use ColdSpring to "wire" up your objects, or dynamically do the injecting for you. We're not using a framework for this series, so we're doing it ourselves. 

Another way we could Inject the WebsiteDAO into the Website service is in the INIT itself. Depending on the relationship (some recursive relationships make it impossible of course), and the complexity, this is an option, but again, for simplicity, instead of re-writing our code, I'm trying to keep building on top of it. So I didn't want to change the way we Init'ed the Website service, just inject it using the setDAO() method.

Ok, now after some theory, and a little coding, lets run our tests, and see what happens.

Our test passes, because we have successfully injected our WebsiteDAO into our Website service, and made the call to getWebsite from the DAO and returned a query. Lets improve our test a little more, by making sure it is not an empty query.

function testGetWebsiteSuccess() {
?    $assert.typeOf( "query", website.getWebsite(4), "Query Expected - Not Received" );
    $assert.assert( Website.getWebsite(4).recordcount >= 1, "Query with 1 or more Records expected");
}

 

The second assertion checks to make sure we get at least 1 record back, this is our test for testGetWebsiteSuccess() so we want to make sure we get a record back. We run the test, and this fails, and we get our message back? Query with 1 or more Records expected.

This post got filled up with a lot of theory, so I think we'll cut this one short. Next time, we'll replace our Dependency with a Mocked version, and complete our Unit Test the way it was supposed to be, with no dependencies on methods or functions, just the function we are testing.

Thanks for reading,

Gavin

08
January 2014

Gavin Pickin

Unit Testing 04 - Testing with Dependencies

Unit Testing

Welcome back to our Getting Started with Unit Testing - Unit Testing for Dummies you might say, and we're onto our fourth post. We have covered setting up our ColdFusion project (with files on Github) and added TestBox as our Testing Framework. We wrote a couple of TDD tests for our objects, and then wrote the objects to pass them.  Then we did some more TDD by writing a more complex test with 10+ assertions, and walked through building our object function to pass the test

Today we're going to look at methods / functions which have dependencies on other objects. Up to this point we have passed information into our methods / functions, which means it is easy to control the test of those methods / functions. Remember that one of the keys to Unit Testing is that we want to isolate each UNIT of code so it is independent of others Units. This obviously becomes more difficult when the code itself is obviously dependent on other components / objects, so we'll look at how to deal with that.

Step 1 - Create a test for a New Dependent Function 

In our application, our Website object is going to have several functions that rely on other functions within itself, these are dependencies. One function will be addDomainName(website_id). This will add a Domain Name to a Website… but it will validate it first. We will use our isValidDomainName() function we used before in this Object, and another, WebsiteDAO.addDomainName(website_id) which will perform the actual Database Activity. 

So, first lets make our new test.

function testSaveDomainNameSuccess() {
    $assert.assert( Website.saveDomainName(domainName) == true, "True was expected, but False was returned - Means there was an Invalid Domain Name Given");
}

This is going to test that our function saveDomainName exists, and that it accepts our domainName object, and when it saves, it returns true.
So lets create our domainName struct we're going to pass.

var domainName = {};
domainName.domainid = 3;
domainName.url = "www.mydomainname.com";
domainName.websiteid = 5;

 

We create a struct, and give it a domainid, url, and a websiteid. And then we pass that into our function.
Lets save, and run our tests, and make sure it fails… and it does as expected… as the saveDomainName function is not created yet.

Step 2 - Create our saveDomainName function to satisfy the test

Lets create our function, saveDomainName, and we'll build it to pass the test. We'll start small, and we'll iterate to make it do all we need it to do. To start, we'll just return true or false, based on if the domainName.url is valid or not, by calling our method in our previous post.

blog04/Step2/model/Website.cfc

function saveDomainName(domainName) {
    return isValidDomainName(domainName.url);
}

 

Now, if we run the test, the test passes, because the domainName struct we passed through had a valid domain. 
Lets update our test to pass an invalid Domain name

blog04/Step2/test/unit/Website.cfc

function testSaveDomainNameSuccess() {
    var domainName = {};
    domainName.domainid = 3;
    domainName.url = "www.2mydomainname.com";
    domainName.websiteid = 5;
    $assert.assert( Website.saveDomainName(domainName) == true, "True was expected, but False was returned - Means there was an Invalid Domain Name Given");
}

 

Now, if we run our tests, our tests fail… which is as expected… although, this works, this defeats the purpose of Unit Testing, because we want to test a Unit of code, which is just our saveDomainName() code, we don't want to retest our isValidDomainName() function, over and over again. 

Step 3 - How do we Resolve our Dependencies

So, how do we solve this?

This is where Mocking comes in. Mocking is where we create fake / mocks of our methods, and we trick our CUT or component under test, to call our fake method, and not the real method. This allows us to control our tests, and remove those dependencies.

To use Mocking, while using TestBox, is really easy. TestBox ships with MockBox included, because these two *Box products go hand in hand. First, in our beforetests() function, we need to create an Instance of MockBox to use in our tests, so lets update our beforetests() function in the blog04/Step3/test/unit/Website.cfc file

function beforeTests() {
    variables.mockbox = new testbox.system.testing.Mockbox();
    variables.Website = new blog04.Step3.model.Website().init();
}

 

As you can see, we added a variables.mockbox which is a new object, and the path is text box.system.testing.Mockbox. If you are using Coldbox it is coldbox.system.testing.Mockbox. I also noticed my bad form, and added variables. to the Website variable, to be consistent.

Now, before all the tests, we create a MockBox instance, and a Website Instance, and we're good to go. Next step is, update our testSaveDomainNameSuccess() test to make our CUT component under test to call our Mocked method isValidDomainName() instead of the real one.

First thing we have to do, is turn our Website component into a Website component with all the magic Mockbox functions we need, to Mock what we need to Mock. You could do it without this step, but you would have to inject methods, and properties into the component yourself, but that is what Mockbox does so well, so we'll just use Mockbox the way it should be used (as far as I am aware, I'm not an expect, so correct me if I'm wrong of course).

variables.mockbox.prepareMock(Website);

 

This calls our Mockbox instance, and passes our instance of Website to the prepareMock(). Now our Website instance has all of the magic methods, including the one we will be using, the .$() method. 
We're going to call the $() method, and pass a couple of params. 

method - the name of the method we are going to mock, in this case, isValidDomainName
returns - the result of calling this method, in this case, true because we want our test to pass, the domain name itself is valid for all we care

That looks like this.

Website.$(method="isValidDomainName", returns=true);

 

2 lines, and we have MockBox'ed our Website, and added the mocked method, and no matter what our CUT component under test passes that function, it will return true.

Lets save it, and run our tests, and see if our testSaveDomainNameSuccess test passes.

Yes, it passed… but if you look, you might have noticed, another test now fails.
The testValidDomainName fails, because one of our assertions, which should return false actually returns true, why? 

Our Mocked Method is still being used, instead of the real method for that test.

So we can use the setup() and teardown() methods, if we want to create a new copy of the Website for each and every test, but, since we're not always going to be mocking it, I'm going to just reset the variables.Website instance, by creating a new instance and overwriting it.

// Reset variables.Website so there are no mocked methods for other tests
variables.Website = new blog04.Step3.model.Website().init();

 

This means when we're done with this test, at the end, we save a new instance in the Website variable, and all future tests will run like they should. 
Save, and run tests, and they all pass, as expected.

Today we have dealt with internal dependencies, and to be honest, I think they are the hardest, since you have to worry about resetting the instance because as you mock methods, you overwrite the originals, with external dependencies, you can mock them, and they're done.

We'll build on our saveDomainName() function more in the next post, as we build out the method to actually deal with checking for an existing Domain Name, and inserting or updating based on that.

Thanks for reading, hope this helps, and as usual, feedback is appreciated, because we are always learning.

Gavin

07
January 2014

Gavin Pickin

cfObejective 2014 - Relax, My Website was Not Hacked - Just Promoting cfObjective

cfObjective, Chit Chat

For those of you who might have noticed, a couple of new banners appears on my very PLAIN and DULL looking blog / website. I know hacking is a popular response to such oddities these days, so I wanted to make sure I posted to let you know it was not a hacker, it was me. I have been waiting for these badges to be releases, so I could show a little color, and pride, as I am a speaker at this years cfObjective in Bloomington MN.

For those who didn't see it, I have a few posts on cfObjective now, including the last one where I announced I was chosen as a speaker

The banners are not just for speakers, but for anyone and everyone who is sponsoring, speaking, attending, or wish they were attending, or just wanting to spread the word... so go on, do your part and spread the word. It is a great conference, and its going to be even better this year, especially if you make it there.

If you wanted a final selling point, Scott Stroz isn't going to be there (sadly), but on a positive note, ou might be able to sweet talk Dave Ferguson into being his guest special host for their live show, and steal Scott's job lol :) Tangent over.

 

You can get the banners here http://www.cfobjective.com/promotional-badges/ but for those not wanting to click on a link, here they all are, right here... including the code to paste them on your site, or in your email.

 

This is a 468x62 banner suitable for headers, footers and email signatures. The HTML follows the banner:

(PNG)
<img border="0" height="62" width="468" alt="" src="http://cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_468x62.png" />

These are 125x125 badges, suitable for sidebars. The HTML follows each badge:

(PNG)
<img border="0" height="125" width="125" alt="" src="http://cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_generic_125x125_w.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_generic_125x125.png" />

(PNG)
<img border="0" height="125" width="125" alt="" src="http://cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_attendee_125x125_w.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_attendee_125x125.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_speaker_125x125_w.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_speaker_125x125.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_sponsor_125x125_w.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_sponsor_125x125.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_media_125x125_w.png" />

(PNG)

<img border="0" height="125" width="125" alt="" src="http://www.cfobjective.com/cfo/assets/Image/badges/2014/CFObjective_media_125x125.png" />

02
January 2014

Gavin Pickin

Unit Testing 03 - More TDD - Building out our Component Objects

Unit Testing

The first couple of Unit Testing posts have got us setup with TestBox and we have written our first real tests for our components. Now it is time to build up our components even more, and in the TDD way, we'll create a few specs, create the tests, then build the methods in our components to pass the tests.

If you haven't followed along, you can go back to Unit Testing 01, and Unit Testing 02, and get the files from my github repo, and then follow along. Step 0 is our base, where we left off from last time, Step 1 is about to begin for blog post 03.

Step 1 - Add testValidDomainName() Test

This app is the start of our Website Manager tool, to help us maintain information about websites, which server they are running on, what cfml engine, and of course, they have domain names attached to them. We need to ensure we validate the domain names so they follow domain name design, but since you could be working with a hosts file for your domain names, not all the rules apply, but these are the ones we're going to look for.

  • Does the domain name only contain letters, numbers, dashes "-" and periods "."
  • Does the domain name segments (sections delimited by periods) Start with Letters
  • Does the domain name segments end with Letters
  • Does each segment have at least one character

We could obviously check for length of the segments, but without more testing into what the hosts file will allow, at this time, we'll assume any length greater than or equal to 1 is sufficient.
So, lets create our test for these assertions.

The Website Object will be the one validating Domains for a Website, so we'll open blog03/step01/test/Website.cfc to add the new test.

function testValidDomainName() {

$assert.assert( Website.isValidDomainName("myTestDomain.com") == true, "True was expected, but False was returned - This is a valid Domain name");
$assert.assert( Website.isValidDomainName("myTestDomain?com") == false, "False was expected, but True was returned - Question Marks are Invalid Characters");
$assert.assert( Website.isValidDomainName("myTestDomain/com") == false, "False was expected, but True was returned - Forward Slash are Invalid Characters");
$assert.assert( Website.isValidDomainName("myTestDomain com") == false, "False was expected, but True was returned - Spaces are Invalid Characters");
$assert.assert( Website.isValidDomainName("1myTestDomain.com") == false, "False was expected, but True was returned - Segments must start with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain.2com") == false, "False was expected, but True was returned - Segments must start with a letter");
$assert.assert( Website.isValidDomainName("-myTestDomain.com") == false, "False was expected, but True was returned - Segments must start with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain.-com") == false, "False was expected, but True was returned - Segments must start with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain1.com") == false, "False was expected, but True was returned - Segments must end with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain.com2") == false, "False was expected, but True was returned - Segments must end with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain-.com") == false, "False was expected, but True was returned - Segments must end with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain.com-") == false, "False was expected, but True was returned - Segments must end with a letter");
$assert.assert( Website.isValidDomainName("myTestDomain..com") == false, "False was expected, but True was returned - Segment must have at least one Character");
$assert.assert( Website.isValidDomainName("myTestDomain.com.") == false, "False was expected, but True was returned - Segment must have at least one Character");
$assert.assert( Website.isValidDomainName(".myTestDomain.com") == false, "False was expected, but True was returned - Segment must have at least one Character");

}

 

Now, you can see, there are a lot of assertions here. They are all testing the same method, the isValidDomainName method, all with different criteria. They are edge cases that you might throw at your code manually when testing a form, what if I do this, what if I do that. If you were doing this in a browser, it would take a lot of time, to try each of these out, but it only took a few mins to write these up, and now anytime we change anything, we can run these tests and make sure our validation logic is solid.

So we go and run the test now, and… since this is TDD, it fails :)

Step 2 - Add isValidDomainName() Method

So now we're going to go and add the method to the Website.cfc component.
So we open the file blog03/step2/model/Website.cfc

First we add the method, and set it to return true always… which is the default response, and we'll add validation shortly to return false if any of the reasons why the passed in Domain name is invalid are found.

function isValidDomainName(domainName) {
    return true;
}

 

Now, we run the tests, and we see, 1 of our assertions passed, and the rest fail.
Our first assertion is that if we pass a valid Domain name, it will be valid, and return true, this is correct, so this assertion passes.

Our next assertion, we pass a domain name with a question mark in it. 
This is supposed to pass back false, this is not a valid domain name. Since our method currently only passes back a TRUE, this fails, as you can see by the screenshot below.

Now, we want to add some logic, that checks the passed in Domain Name for invalid characters.
So we update our method to check for invalid characters

function isValidDomainName(domainName) {
    if (refind('[^A-Za-z\-\..]', domainName)) {
        return false;
    }
    return true;
}

 

Now, this should catch any domain name with anything other than A-Z, a-z, dash - and period .
So we run our tests again, and where does it fail now?

As you can see, it fails on this assertion - False was expected, but True was returned - Segments must start with a letter
That is our 4th assertion. So our if statement just passed 3 assertions, ?, / and a space " ". That is encouraging. 

Step 3 - Lets check our Domain Name Segment for Starting and Ending Characters

Now, we need to check each segment, and see if they all begin with a letter… so we're going convert the domain name from a list with a period delimiter, into an array, and loop over it. And we'll check the left most character, and see if its one of our A-Z or a-z characters.

function isValidDomainName(domainName) {
    var i = "";
    var segmentArray = "";

    if (refind('[^A-Za-z\-\..]', domainName)) {
        return false;
    }

    segmentArray = listToArray(domainName, ".", true);
    for (i = 1; i <= arrayLen(segmentArray); i++){
        if (refind('[^A-Za-z]', left(segmentArray[i],1))) {
            return false;
        }
    }

    return true;
}

 

Now, we save this, and run our tests again, and the failing assertion is - False was expected, but True was returned - Segments must end with a letter.

That is our 9th assertion, which means that new validation check catches 4 false domain names, and returns false correctly.
Now, we need to do the same type of check for the last letter of a segment, so we'll just add it to the loop, to be efficient.

So after the first if statement in the for loop, add the following if statement.

if (refind('[^A-Za-z]', right(segmentArray[i],1))) {
    return false;
}

 

Now the failing assertion message is - False was expected, but True was returned - Segment must have at least one Character… which is our last assertion, so that change correctly identified another 4 invalid domain names. The last assertion we need to check for, is empty segments. 

Now, there is a slight trick to this… usually when you convert lists to arrays, it ignores empty items, but the TRUE on the end of the listToArray function is includeEmptyFields. Without that, our code would never see an empty segment.

Ok, so while we're in the same for loop, we'll add another if statement, this time checking the length of the segment, if its 0 we will return false. If it is 1 or more, for our specs, it is valid.

if (len(segmentArray[i],1) == 0) {
    return false;
}

Now, we run our tests, and what do we see?

A big fat error… because I missed something, when I copied and pasted, and edited the above if statement. Yup, I make mistakes, and you might too, so I modify the if statement, so the len function is passed a string as the only argument.

if (len(segmentArray[i]) == 0) {
    return false;
}

 

And now, as you can see below, the tests pass.

We have successfully added the test to make sure our isValidDomainName function exists, and works to our specifications. Its a pretty simple example, but it shows how you can make sure you check off all the specs as you go through, and every new spec just adds to the list, making your code better and better all the time.

Check back soon for the next post in the series, soon we're going to look at databases, services and mocking, to make our Unit Testing more thorough than the basics we've touched on so far. 

Thanks for reading,

Gavin

 

01
January 2014

Gavin Pickin

Deals and Steals - Books and Training - Review of OOP for CF and Ebook Sale Ending

Books and Training, Steals and Deals

Its the 1st of January, 2014, and its time for some resolutions, and decisions, and today, I'm reminding you of an important one. Leveling up your skills... Packt Publishing's $5 Ebook sale is ending Jan 3rd, so its your last chance to pick up some of those amazing books, for only $5, you just can't beat that deal.

Today I bought 10 books, some of them I discussed here in my first post on the Ebook-bonanza from Packt Publishing but I decided to look a little more and picked up a couple more. Today, I busted open the first one, one I have been meaning to read for a LONG time now, so I finally did. It was a good read, and actually finished it today. Matt Gifford's Object Oriented Programming for ColdFusion... so let me tell you what I thought about it.

With the Foreword written by Ben Forta, Ben Nadel, and Terry Ryan, and reviewed by Raymond Camden, it was a promising sign. It was the first real OOP book for ColdFusion, and there have been a few since then, but the concepts in this book have definitely held their own. This is supposed to be a beginner guide, discussing the concepts and how they apply... so I found it a great refresher, and really liked the fact it was an easy read, and I didn't need to dive into the code example downloads to be able to follow along. I read it on my ipad, and the digital version was pretty well laid out, so that was nice... although no code indentation, was not ideal, but we're all had to deal with that in our time.

I liked the fact it was broken down into each piece of the OOP design, introducing ColdFusion Components, Effective Component Development, Building Beans, Inheritance and OO Concepts, DAO, Gateways, and Services. Each chapter built on a small sample app, refactoring as you went, which I thought was a good way to ensure the reader could follow along, Sometimes, some books use so many different examples, you might not catch the small things, because you are not as clear on the application structure, and requirements.

Although the book was setup using DAOs and Gateways, it had a discussion about the use of DAOs and Gateways being 2 components, compared to combining them, and used references to Java and references to Fowler to give you some of the reasoning behind both styles, and basically stating it was up to you, and your project. I thought this was good, as I ran across a discussion about this on Twitter recently, Scott Stroz (host of CFHour) who (practically) never separates the DAO and Gateway, and others, that do it regardless of the project. The section on this helped me clarify reasons where you might want to split them, but in the end, its up to you.

One of the only negatives, call it a pet peeve is some of the editing, (as Sean Corfield mentioned in his Amazon review), the function names like save() were switched out with saveUser() several times, and information had a L in it, and other bad editing was obvious in a few places. I'm sure after all this time, these changed have been submitted by readers, but obviously, no one has gone back in to update the material. Nothing earth shattering, but still, one of those things, that takes the polish off an otherwise great book in my mind.

I think its a great book, readable in a day (even for a slow reader like myself) and a good solid reference to all of basics of OOP for CF. If you think you have a firm handle on this, you might need to look more into real OOP Design Patterns, but its great for converting OOP over to ColdFusion concepts.

If you haven't read it, go pick it up, its only $5, like every other EBook on Packt Publishing Site right now.
If you have read it, go pick up a few others... and invest in your own skillset.

Happy New Year,

Gavin

Blog Search