Showing posts with label AngularJS. Show all posts
Showing posts with label AngularJS. Show all posts

Sunday, August 24, 2014

Functional Programming All the Things OR How to Use Curry to Make Your Test More Readable

"I read in's looks"
-- Shakespeare, Henry VIII
Act I, Scene I, Line 125.2

We, the software craftsmen, understand that tests are a first class citizen.  If the system code was deleted it would be a great opportunity to rewrite the system and know once all the tests pass that the system has all the same functionality.  The question is should tests have the same style as the system code?

Say you have the Coin Changer kata and you want to use a function which takes both the coin value and the amount of change to be given.

If we wrote this in a C-style interface it look something like the following:

int[] changeFor(int[] coins, int amount)

While testing this we have a choice: we can either pass in the coins each time in the test thus violating the DRY principle or we could some how fill in the coins we want to use on each test in a testing series.  The second choice is very interesting since it is screaming for currying the coins per testing series.  Following this idea we have the following for the Coin Changer kata using AngularJS.



We have the following for the system code of the changeFor function.

    $scope.changeFor = function(coins, amount) {
      if (_.isEmpty(coins)) return [];

      return _.reduce(coins, function(m, coin) {
        m.change.push(Math.floor(m.amount / coin));
        m.amount %= coin;
        return m;
      }, {
        amount: amount,
        change: []
      }).change;
    };

We see that lodash's isEmpty makes sure that we have an array of coins before we go to the aggregation using the reduce function.  In the reduce we create an object with the current amount and change calculated so that we do not have to mutate the values passed in to the changeFor function.

We test the cases when we have no coins being passed in with the following code from appSpec.js.

describe('given no coins', function() {
var noCoinsFor;
beforeEach(function() {
noCoinsFor = _.curry($scope.changeFor)([]);
});
it('should return nothing for 0 cents', function() {
expect(noCoinsFor(0)).toEqual([]);
});
it('should return nothing for 99 cents', function() {
expect(noCoinsFor(99)).toEqual([]);
});
});

We set up changeFor function by currying with an empty array for the coins in the beforeEach in the top level describe.  Using curry in the beforeEach we do not have to pass in the empty array on each call in the its.  This makes the test a bit more readable.

Similarly when we test the penny series we can curry the penny.

describe('given pennies', function(){
var penniesFor;
beforeEach(function(){
penniesFor = _.curry($scope.changeFor)([1]);
});
it('should return nothing for 0 cents', function(){
expect(penniesFor(0)).toEqual([0]);
});
it('should return 1 penny for 1 cents', function(){
expect(penniesFor(1)).toEqual([1]);
});
it('should return 2 pennies for 2 cents', function(){
expect(penniesFor(2)).toEqual([2]);
});
});

This makes the grouping in the describe seem more logical and the test more readable.

The same with the nickel and penny.

describe('given nickels and pennies', function(){
var nickelsPenniesFor;
beforeEach(function(){
nickelsPenniesFor = _.curry($scope.changeFor)([5, 1]);
});
it('should return nothing for 0 cents', function(){
expect(nickelsPenniesFor(0)).toEqual([0, 0]);
});
it('should return 1 penny for 1 cents', function(){
expect(nickelsPenniesFor(1)).toEqual([0, 1]);
});
it('should return 1 nickel and 1 penny for 6 cents', function(){
expect(nickelsPenniesFor(6)).toEqual([1, 1]);
});
});

Finally our integration tests for US coins.

describe('given quarters, dimes, nickels, and pennies', function(){
var changeFor;
beforeEach(function(){
changeFor = _.curry($scope.changeFor)([25, 10, 5, 1]);
});
it('should return nothing for 0 cents', function(){
expect(changeFor(0)).toEqual([0, 0, 0, 0]);
});
it('should return 1 penny for 1 cents', function(){
expect(changeFor(1)).toEqual([0, 0, 0, 1]);
});
it('should return 3 quarters, 2 dimes, 0 nickels and 4 pennies for 99 cents', function(){
expect(changeFor(99)).toEqual([3, 2, 0, 4]);
});
});

By using the curry function we have added to the readability of the Jasmine tests.  This style is a bit different than the system code in the AngularJS application.

Check out the plunker which goes with this post.


Saturday, July 26, 2014

Look Mom I'm FizzBuzzing in Chrome OR Intro to AngularJS

"Like captives bound to a triumphant car."
-- Shakespeare, Henry VI Part I
Act I, Scene I, Line 22

Tell me what you program in and I'll tell you how to FizzBuzz in it.

Not being an "UI guy" but wanting to become a Full Stack Developer the question came up, how do I do Code Katas using a more full stack approach?  How would I do FizzBuzz using AngularJS?

The great thing about AngularJS is that you can simply add it to whatever HTML you currently have meaning you can add it to your html tag, body, or even just a simple div.

Say you have some HTML like the following.


<html> <head> <link href="style.css" rel="stylesheet"></link> </head> <body> <h1> ng-fizzbuzz</h1> <div> <input /> <h1> FIZZBUZZ_RESULT</h1> </div> </body> </html>
You would like have an input box which will display the FizzBuzz value, thus given 3 in the input box will display Fizz and likewise 2 would give 2.  Let's add some simple JavaScript using AngularJS to FizzBuzz this.



Looking at this gist we see the following in the HTML:



We see we have three parts

  • ng-app => which Angular application are we going to use
  • ng-controller => which controller on the application are we going to use
  • ng-model => what data on the controller are we going to use
These parts align with the JavaScript code:


var app = angular.module('fizzbuzzApp', []);
 

names the application


app.controller('FizzBuzzCtrl', ['$scope',
function($scope) {
$scope.translate = function(value) {
var ret = "";
if (!value) return "";
if (value % 3 === 0) ret += "Fizz";
if (value % 5 === 0) ret += "Buzz";
return ret || value;
};
}
]);


defines the function for the controller

We do not see the value defined in the ng-model anywhere in the JavaScript code, so what gives?

{{translate(value)}}

calls the translate on the controller giving it the value of value bound in the ng-model on the input tag

This is one of the easier ways to do FizzBuzz with AngularJS.

This code is on Plunker: http://plnkr.co/edit/HMxS8H?p=info

Enjoy.