Blog

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

Blog Search