12/17/2012

Building a Unit Test in Jasmine, Part 4

The last part in this series is going to involve some time travel.  Physics aside, let's assume six months have passed since our original JasmineTester object was created.  The object was a great success and has been embedded all throughout the organization.  We have forgotten the details of our earlier work as multiple other projects have come and gone.  While still basking in the glow of our earlier success, a new requirement comes down the line: make a new object exactly like the JasmineTester but with multiplication.  Thanks goodness we used Jasmine!

Our new TDD development style tells us to start with the tests.  We create a new test file for our new object and get started.  The fiddle for this post continues to keep everything together for simplicity and ease of reference.

The first thing is to start our suite for this object:
describe('JasmineTesterExtended Tests'function() {
    var testObjectExtended;

    //before each test (it) rebuild the object we are testing against
    beforeEach(function() {
        testObjectExtended new JasmineTesterExtended({
            a2,
            b2,
            c2,
            d2,
            e2
        });
    });
}); 

The new suite will be a little bit different.  In the six months since we have learned about beforeEach.  beforeEach is a great mechanism to keep the tests within a suite from affecting each others' outcomes.  The beforeEach function will run "before each" test.  The object instance is going to be recreated fresh for every test.  The object is still declared at the top of the suite so it is in scope for all tests.  The object instance in this suite is going to mirror the original JasmineTester object since we don't want to change the constructor.

Time for the first test:  
   
//make sure the total method still works as expected
    it('Check inherited method total equals 10'function() {
        expect(testObjectExtended.total()).toEqual(10);
    });

This test is going to ensure that the inherited components are maintained no matter what we do to the new object. We run our test and get the expected failure. Time to open the source file and create the JasmineTesterExtended object.

We are going to create the object using the Constructor Inheritance pattern described by Nicholas Zakas in "Professional JavaScript for Web Developers, Third Edition" (Wrox Press).
function JasmineTesterExtended(opts{
    JasmineTester.call(thisopts);
}
JasmineTesterExtended.prototype new JasmineTester();

Once created we return to our tests and run them. This time we have success. The next requirement is to add a multiplication object. The new multiply function should take all valid numbers in the object and multiply them together. This is the test we create:
    //test the multiply function
    it('JasmineTesterExtended should multiply to 32'function() {
        expect(testObjectExtended.multiply()).toEqual(32);
    });

The test should run and fail. A natural question is, "Why bother running the test first when we know it is going to fail?". The answer is that not running the test is assuming it is going to fail. Assumptions in programming don't end well. In this simple object, we know that there is no risk of conflict, but in bigger projects that may not be the case. An object, function, or process you are using may have dependencies that you aren't aware of, or may already exist. Running the test first ensures that there is no pre-existing conflict.

Leveraging the lessons learned from the total function we add the if and typeof checks right away.   
var total = 1,
        num;

    for (num in this{
        //make sure we only process this object's direct properties
        //make sure this property is a number
        if (this.hasOwnProperty(num&& typeof this[num=== 'number'{
            total *= this[num];
        }
    }
    return total;
};

We return to our test, run, and success!  In production, we would create the same validation tests for the if and typeof checks to be thorough.

I'm happy to report that unit testing and I are still doing well and the relationship continues. The benefits of unit testing are so significant that I can't imagine coding without it. On our way out, let's summarize some of the benefits we uncovered during our series:
  • Keeps code modular and focused
  • Describes a concrete goal to code towards before starting your code
  • Helps capture errors that new code changes can cause to earlier functionality
  • Protects earlier work from being compromised
  • Simplifies regression testing
  • Protects against knowledge loss and personnel changes



Other parts in the series: