Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Sunday, October 12, 2014

Let's Build an AngularJS App!

This weekend I had the pleasure of attending the Atlanta Code Camp. It was a fantastic venue with sold out attendance and a variety of speakers covering cutting edge topics. I always try to submit a variety of topics but due to popularity, AngularJS won again! I've watched many talks that break down various facets of Angular and even some live coding demonstrations, but realized there haven't been many that cover building an actual, functional app with unit tests.

I structured the talk so that I could easily go from start to finish in stages. In the PowerPoint deck you'll see I have actual git checkout commands that reference the various commits. This will enable you to roll forward to each step after you've cloned the repository. The result is a responsive health calculator app that scales well to a variety of page sizes, has built-in validations for inputs and provides you with basal metabolic rate (BMR), body mass index (BMI), and target heart rate zones (THR). The app was developed using a test-driven (TDD) approach with a total of 83 specifications. The GitHub repository has full source and instructions.

Here is the deck. I hope you enjoy!




Jeremy Likness

Saturday, October 4, 2014

AngularJS Tip: Using a Filter with ngClass

I was working on my presentation of building an AngularJS app for the upcoming Atlanta Code Camp and ran into an interesting scenario. The app tracks various variables and formulas and then displays them in a responsive fashion. I’ll post the deck and code after the camp. One formula is Body Mass Index, an indication of general weight and health. I use it as an example but it is not the most accurate and a lean, muscular person may register as overweight or obese on the scale (read my non-technical article 10 Fat Mistakes to learn what you should really focus on).

For the session I want to show a test-driven approach, so I start out by defining what I expect from the formula using Jasmine:

describe("Formula for BMI", function () {
 
    describe("Given a 5 ft 10 person who weighs 300 pounds", function () {
        it("should compute a BMI of 43", function () {
            var actual = formulaBmi({
                height: 70,
                weight: 300
            });
            expect(actual).toBeCloseTo(43);
        });
    });
    describe("Given a 5 ft 8 in person who weighs 120 pounds", function () {
        it("should compute a BMI of 18.2", function () {
            var actual = formulaBmi({
                height: 68,
                weight: 120
            });
            expect(actual).toBeCloseTo(18.2);
        });
    });
});

Next I define the formula and ensure the test passes (notice at this stage I haven’t even involved Angular yet):

function formulaBmi(profile) {
 
    // BMI = (weight in pound * 703) /
    // (height in inches) ^ 2
    var bmi = (profile.weight * 703) /
        (profile.height * profile.height);
    // round it
    return Math.round(bmi * 10.0) / 10.0;
}

So that:

  • Formula for BMI
    • Given a 5 ft 10 person who weighs 300 pounds … should compute a BMI of 43
    • Given a 5 ft 8 in person who weighs 120 pounds … should compute a BMI of 18.2

Then I set the expectation it will register with Angular as a service:

describe("BMI Formula service", function () {
    var formulaBmiSvc;
    beforeEach(function () {
        module('healthApp');
    });
    beforeEach(inject(function (formulaBmiService) {
        formulaBmiSvc = formulaBmiService;
    }));
    it("should be defined", function () {
        expect(formulaBmiSvc).not.toBeNull();
    });
    it("should be a function", function () {
        var fnPrototype = {},
            isFn = formulaBmiSvc &&
            fnPrototype.toString.call(formulaBmiSvc)
            === '[object Function]';
        expect(isFn).toBe(true);
    })
});

… and I register the function with Angular to make the test pass:

(function (app) {
 
    app.factory('formulaBmiService', function () {
        return formulaBmi;
    });
})(angular.module('healthApp'));

So that:

  • BMI Formula Service
    • should be defined
    • should be a function

To show the index I want to translate it to the scale it provides. Anything under than 18.5 is considered underweight, while anything over 25 is overweight and over 30 is considered obese.

Now I can write the tests for a filter that satisfies these conditions:

describe("BMI filter", function () {
    var bmiFilter;
    beforeEach(function () {
        module('healthApp');
    });
    beforeEach(inject(function ($filter) {
        bmiFilter = $filter('bmi');
    }));
    it("should be defined", function () {
        expect(bmiFilter).not.toBeNull();
    });
    describe("Given BMI is less than 18.5", function () {
        it("should return Underweight", function () {
            var actual;
            actual = bmiFilter(18.4);
            expect(actual).toBe('Underweight');
        });
    });
    describe("Given BMI is greater than or equal to 18.5 and less than 25", function () {
        it("should return Normal", function () {
            var actual;
            actual = bmiFilter(18.6);
            expect(actual).toBe('Normal');
        });
    });
    describe("Given BMI is greater than or equal to 25 and less than 30", function () {
        it("should return Overweight", function () {
            var actual;
            actual = bmiFilter(26);
            expect(actual).toBe('Overweight');
        });
    });
    describe("Given BMI is greater than or equal to 30", function () {
        it("should return Obese", function () {
            var actual;
            actual = bmiFilter(31);
            expect(actual).toBe('Obese');
        });
    });
});

… and the filter itself:

(function (app) {
 
    app.filter('bmi', function () {
        return function (input) {
            var value = Number(input);
            if (value >= 30.0) {
                return 'Obese';
            }
            if (value >= 25.0) {
                return 'Overweight';
            }
           if (value < 18.5) {
                return 'Underweight';
            }
            return 'Normal';
        };
    });
})(angular.module('healthApp'));

So that:

  • BMI filter
    • should be defined
    • Given BMI is less than 18.5 … should return Underweight
    • Given BMI is greater than or equal to 18.5 and less than 25 … should return Normal
    • Given BMI is greater than or equal to 25 and less than 30 … should return Overweight
    • Given BMI is greater than or equal to 30 … should return Obese

Now that I have the filter, I can use it to show the text for the current BMI:

<h1>BMI:</h1>
<
h2>{{ctrl.bmiValue}}</h2>
<
b>{{ctrl.bmiValue | bmi}}</b>

This will show the title, the actual numeric BMI value and the designation of Underweight, Normal, Overweight, or Obese.

The problem I have is that I want to apply a class and show it in light red for the underweight and overweight conditions, and dark red for the obese condition. I could use ng-class and simply redefine the ranges like this:

<div ng-class="{ obese: ctrl.bmiValue >= 30 }"></div>

… but that feels like duplicating code. Fortunately, the ng-class directive supports having an expression passed in. What it evaluates to is what it will use as the class. So, I simply define it like this:

<div class="tile"
     ng-class="ctrl.bmiValue | bmi"
     title="Body Mass Index">...</div>

And ensure my CSS is set up correctly :

div.Obese {
    background: red;
} div.Overweight {
    background: lightcoral;
} div.Underweight {
    background: lightcoral;
}

Now I’m good to go. If you are concerned that the classes don’t follow convention (they are uppercase) and maybe need a more unique name (such as a prefix to avoid collisions) the solution is simple: add a parameter to the filter and based on that parameter, return the readable text or the translated class. Either way you encapsulate the logic in one place for reuse throughout your app.

Here’s a snap of the result scaled to a mobile form factor:

healthcalculator

If you are in the Atlanta area and reading this before October 11, 2014, please come join us at the code camp. It is a day loaded with great sessions. Until then, enjoy your Angular coding!

signature[1]

Tuesday, June 25, 2013

Improving the Readability of Windows Store Unit Tests

I’ve grown to appreciate the value that testing adds to software development. In my opinion many developers spend far too much time focusing on the red herring of code coverage or writing tests just to check them off, rather than the real benefits they provide when you take a more Test-Driven Development (TDD) approach. Although I’ll be the first to admit I don’t follow strict TDD in all of my projects, I have found that having the discipline to write your tests first as often as possible can save a lot of time on a project and result in a much cleaner API. It’s the difference between building a class and exposing what you think the API should look like, versus writing a test that uses the API the way you would want to consume it, then writing the class to fulfill the need.

One issue I have with tests, however, is that the Microsoft method for testing isn’t very clear. For example, take a look at this:

Assert.IsTrue(2 > 3);                 

How would you describe this test to someone? “assert it is true 2 is greater than three” is how it reads. How about this instead?

Assert.That(2, Is.GreaterThan(3));

That now reads, “assert that 2 is greater than 3”. Doesn’t that describe exactly what you are checking (and hoping will fail?) This becomes even more confusing when you are comparing equality. Try this:

Assert.AreEqual(2, 3);

Compared to this:

Assert.That(2, Is.IsEqualTo(3));

Again, you may think I’m crazy but I think the second is easier to write and read. It just flows and instead of trying to figure out which assertion method makes sense, then putting the expected or actual values in the right place, etc. why not just write it out in a nice, easy to understand sentence?

It turns out it is fairly easy to build your own library of extensions to the existing framework that make it easy to provide a more fluent interface for testing. There are third-party libraries you can look into but the quick and dirty way is right here. I just decided to ride on the truth assertion and extend that to fit my needs. First, a class that will take some generic object and apply a predicate then optionally display a message:

public static class AssertIt
{
    public static void That<T>(T target, Func<T, bool> condition, string message = ""
    {
            if (string.IsNullOrWhiteSpace(message))
            {
                Assert.IsTrue(condition(target));
            }
            else
            {
                Assert.IsTrue(condition(target), message);
            }
    }       
}

Next, a static class for the comparisons. Note the parent class is expecting a predicate, so the helper class will provide the predicate and manage any optional parameters. Here is the “Is” class with two examples:

public static class Is
{
    public static Func<T, bool> GreaterThan<T>(T comparison) where T : IComparable
    {
        return target => target.CompareTo(comparison) > 0;
    }

    public static Func<string, bool> NotNullOrWhitespace()
    {
        return target => !string.IsNullOrEmpty(target);
    }
}

Each method returns the predicate for the comparison and handles any additional parameters that are needed. This enables you to write something like this:

AssertIt.That(2, Is.GreaterThan(3));                 

Of course we need to test that our tests are going to run appropriately! Here’s a pair of tests that ensure our assertions are working correctly for the “greater than” comparison.

[TestMethod]
public
 void GivenAnIntegerWhenComparedGreatherThanToAGreaterIntegerThenShouldFail()
{
    var exception = false;

    try
    {
        AssertIt.That(2, Is.GreaterThan(3));                
    }
    catch (AssertFailedException)
    {
        exception = true;
    }

    Assert.IsTrue(exception, "Test failed: an exception should have been thrown.");
}

[TestMethod]
public
 void GivenAnIntegerWhenComparedGreaterThanToALesserIntegerThenShouldSucceed()
{
    AssertIt.That(3, Is.GreaterThan(2));            
}

And that’s it – to me it’s worthwhile to invest some time in some extension and helper methods to make the code far more readable and easier to write. You could, of course, just plug in a third-party test framework that already provides a fluent interface like NUnit as well.