The intersection of technology and leadership

Category: Javascript (Page 1 of 2)

The Karma Test Runner’s dreaded Script Error

I have been using Karma Test Runner for javascript testing and like using PhantomJS as the test browser as it runs headless and is super fast. However every so often, I encounter the dreaded line:

Error: Script error

The error often looks like this in the console:

Error: Script Error

Since I work often in small cycles of test-code-commit (or just code-commit), my solution is often to rollback and try again. I thought I’d spend some time trying to work out how to get better diagnostics. A bit of digging around and the suggested way is simply to change browsers that give you a view of the javascript problem.

You do this by changing your karma configuration. In my karma.conf.js file I would tweak the value as below:

Karma.conf.js for different browsers

I then run the test again, and look at Chrome’s in built javascript console and look for errors:

Missing resource

Voila! A HTTP Not Found (404) for a resource I had declared in one of my require scripts. In this example, I intentionally added it in to cause that error, but this technique is useful for when you just can’t work out why something is not working.

Base web project with angular, require, grunt and karma

I was looking for a project that had a good basis for a build that included angular (JS MVC framework), require (for modules), grunt (for build) and karma (for automated tests).

I came across this one in the end that looks like it will do the job: https://github.com/StarterSquad/angularjs-requirejs-seed

I tried angular-seed-requirejs but it lacked tests, angular-seed didn’t have a nice build script and angular-seed-coffeescript and angular-grunt-coffeescript all lacked tests with karma.

It took me a while to find but this project seems to do what I want right now.

Making google analytics work with requirejs

We were trying to template google analytics and make it part of our requirejs setup. When we did what we thought was obvious to wrap it in a module and declare a dependency on it, it seemed to fail quite miserably.

The trick was that we missed that you needed to export a global variable. This means in the javascript file initialising google analytics we had to add the following code block

var registerGoogleAnalytics = function(accountId) {
  var _gaq = _gaq || [];
  if (window) {
    window._gaq = _gaq; // export as global
  }
  _gaq.push(['_setAccount', accountId]);
  _gaq.push(['_trackPageview']);

  // rest of google analytics initialiser script...
}

Some links that helped us working this out:

Making jade and mustache templating work together

One our frustrations using jade and icanhaz (a javascript front end mustache implementation) was that when we were trying things that were obvious to us, jade would simply fail to template and we weren’t sure what was causing it.

Fortunately small TDD cycles and experimentation made us realise that it was the combination of new line characters and mustache code made jade work/break.

We would try something like this:

script(type="text/html", id="my_checkbox", class="partial")
  li 
    label(for="{{code}}")
      {{name}} 
    input(id="{{code}}", checked="checked", name="{{code}}", type="checkbox")

The set of statements above would be valid mustache (once converted to HTML) but jade complains because the {{name}} is on its own line. The fix was to use the pipe (|) character to force jade to recognise a line break. It looks like this now

script(type="text/html", id="my_checkbox", class="partial")
  li 
    label(for="{{code}}")
      | {{name}} 
      input(id="{{code}}", checked="checked", name="{{code}}", type="checkbox")

Simple, but not particularly obvious from the examples in their documentation.

A builder pattern implementation in Javascript

We’re using the builder pattern on our javascript project, as it useful for starting off with a set of defaults, but is clear when we want to override particular values. Although there are a few places on the net that describe who uses the builder pattern in javascript, they don’t really provide an implementation.

Here’s one that works for us:

var Builder = function() {
  var a = "defaultA";
  var b = "defaultB";
  
  return {
      withA : function(anotherA) {
        a = anotherA;
        return this;
      },
      withB : function(anotherB) {
        b = anotherB; 
        return this;
      },
      build : function() {
        return "A is: " + a +", B is: " + b;
      }
  };
};

var builder = new Builder();

console.log(builder.build());

var first = builder.withA("a different value for A").withB("a different value for B").build();

var second = builder.withB("second different value for B").build();

var third = builder.withA("now A is different again").build();

console.log(first);
console.log(second);
console.log(third);

Cheat Sheet for Javascript Testing with Jasmine

Jasmine is the default unit testing framework that I use when writing javascript, however my poor brain can’t always remember all the different ways of getting things to work. There are quite a number of cheat sheets out on the internet including:

They don’t quite cover all the examples as well. Here’s my contributions to demonstrate some of the common uses.

describe("jasmine", function () {

    describe("basic invocations", function () {

        var SampleDependency = function () {
            return {
                usefulMethod:function (firstParameter, secondParameter) {
                },
                anotherUsefulMethod:function () {
                }
            };
        };

        var Consumer = function (dependency) {
            return {
                run:function () {
                    dependency.usefulMethod("first", "second");
                },
                runSecondMethod:function () {
                    dependency.anotherUsefulMethod();
                },
                runWithRequiredCallback:function(callback) {
                    callback("an argument");
                }
            };
        };

        it("should spy on an existing function", function () {
            // given
            var dependency = new SampleDependency();
            spyOn(dependency, "usefulMethod");
            var consumer = new Consumer(dependency);

            // when
            consumer.run();

            // then
            expect(dependency.usefulMethod).toHaveBeenCalled();
            expect(dependency.usefulMethod).toHaveBeenCalledWith("first", "second");
            expect(dependency.usefulMethod).toHaveBeenCalledWith(jasmine.any(String), jasmine.any(String));
            expect(dependency.usefulMethod.callCount).toEqual(1);
            expect(dependency.usefulMethod.mostRecentCall.args).toEqual(["first", "second"]);
        });

        it("should demonstrate resetting of the spy", function () {
            // given
            var dependency = new SampleDependency();
            spyOn(dependency, "usefulMethod");
            dependency.usefulMethod();
            dependency.usefulMethod.reset();

            // when
            dependency.usefulMethod();

            // then
            expect(dependency.usefulMethod).toHaveBeenCalled();
            expect(dependency.usefulMethod.callCount).toEqual(1);
        });

        it("should demonstrate creating a spy object with prepopulated methods", function () {
            // given
            var dependency = jasmine.createSpyObj("dependency", ["usefulMethod", "anotherUsefulMethod"]);
            var consumer = new Consumer(dependency);

            // when
            consumer.run();
            consumer.runSecondMethod();

            // then
            expect(dependency.usefulMethod).toHaveBeenCalled();
            expect(dependency.anotherUsefulMethod).toHaveBeenCalled();
        });

        it("should demonstrate creating a stub object", function () {
            // given
            var dependency = jasmine.createSpyObj("dependency", ["usefulMethod", "anotherUsefulMethod"]);
            var consumer = new Consumer(dependency);
            var stubbedCallback = jasmine.createSpy("stub callback");


            // when
            consumer.runWithRequiredCallback(stubbedCallback);

            // then
            expect(stubbedCallback).toHaveBeenCalled();
            expect(stubbedCallback).toHaveBeenCalledWith("an argument");
        });
    });


    describe("returning a value", function () {
        var Dependency = function () {
            return {
                getMultiplier:function () {
                    return 10;
                }
            };
        };

        var Consumer = function (dependency) {
            return {
                calculateSomethingWithMultiplier:function (number) {
                    return number * dependency.getMultiplier();
                }
            };
        };

        it('should be correct', function () {
            // given
            var dependency = new Dependency();
            spyOn(dependency, "getMultiplier").andReturn(40);
            var consumer = new Consumer(dependency);

            // when
            var result = consumer.calculateSomethingWithMultiplier(3);

            // then
            expect(result).toEqual(120);
        });
    });


    it("should demonstrate creating a stub that returns a value", function () {
    });

    describe("creating a stub that calls a fake", function () {
        var Dependency = function () {
            return {
                request:function (callback) {
                }
            };
        };
        var Consumer = function (dependency) {
            var capturedValue = "";
            return {
                hardAtWork:function () {
                    dependency.request(function (value) {
                        capturedValue = value;
                    });
                },
                getCapturedValue:function () {
                    return capturedValue;
                }
            };
        };

        it('should demonstrate creating a stub function that does something interesting', function () {
            // given
            var dependency = new Dependency();
            spyOn(dependency, "request").andCallFake(function (callback) {
                callback("Controlled return value from callback");
            });
            var consumer = new Consumer(dependency);

            // when
            consumer.hardAtWork();

            // then
            expect(consumer.getCapturedValue()).toEqual("Controlled return value from callback");
        });

    });
});

RequireJS is the Spring Framework of Javascript

I’ve been working on setting up the infrastructure for a mostly javascript based project, and we’ve been putting RequireJS into the codebase to help us manage the file dependencies instead of having to declare them within the page that is using them. As a concept, RequireJS is helping us keep different javascript modules apart in different files and let’s us assemble them.

RequireJS works by declaring dependencies and having the framework pull them in when you need them.

define(["aDependency"], function(theDependency) {
  // now I can do something with theDependency
  theDependency.aMethodOnIt();
})

This is pretty much how spring works, but the issue I have is that RequireJS manages the lifecycle of the javascript objects, so when you want to pass in a substitute for a test, you end up in a dilemma.

define(["aDependency"], function(theDependency) { // how do I get inject a different instance?
  // now I can do something with theDependency
  theDependency.aMethodOnIt();
})

Unsurprisingly a number of people wrote libraries such as testr which allow you to override the requirejs to inject different versions. Although very reasonable approaches, I find this approach a little bit smelly as you’re effectively patching a library you don’t own. The ruby community know the dangers of monkey patching too much, particularly those parts of a code base you cannot control and the potential issues you face when you try to upgrade.

Our current approach involves using RequireJS to manage the file/name dependencies, but for us to write javascript that allows us to control the instances of the objects that we want. Here’s an example:

dependency.js

define([], function () {
    return function () {
        return {
            doSomeWork:function () {
            }
        };
    };
});

consumer.js

define([], function () {
    return function (aDependency) {
        var dependency = aDependency;
        return {
            start:function () {
                dependency.doSomeWork();
            }
        };
    };
});

And then we control the lifecycle of the components and instances in the application using the following code.

main.js

define(["consumer", "dependency"], function (Consumer, Dependency) {
    var dependency = Dependency();
    var consumer = Consumer(dependency);
    consumer.start();
});

And our jasmine tests get to look like this:

requirejs = require('requirejs');

describe("consumer", function() {
    it("should ensure the dependency does some work", function() {
        // given
        var dependency = jasmine.createSpyObj("dependency", ["doSomeWork"]);
        var consumer = requirejs("consumer")(dependency);

        // when
        consumer.start();

        // then
        expect(dependency.doSomeWork).toHaveBeenCalled();
    });
});

This approach has been working out well, forcing us to manage the dependency and global hell that javascript global functions can quickly become. Thoughts? Please leave a comment.

“fs is not defined” in jasmine-node version 1.0.28

I was using jasmine-node this weekend (package.json says it’s version 1.0.28) and hit this error:

../node_modules/jasmine-node/lib/jasmine-node/cli.js:89
        var existsSync = fs.existsSync || path.existsSync;
                         ^
ReferenceError: fs is not defined

It looks like this has already been reported here as Issue #186. The quick fix is to add the require declaration in yourself at the top of the file.

var fs = require('fs');

Goto Aarhus 2012

This year was my first time to both attend and present at Goto Aarhus. Over the years, many of my colleagues have said that it’s one of the best conferences with topics in lots of different areas. This year focused on topics such as NoSQL, Big Data, Humans at Work, Javascript, Continuous Delivery, Cloud and many more areas.

Two of the best presentations I attended, both for content and delivery were Sam Newman and Jez Humble, author of Continuous Delivery (Disclaimer: They are my colleagues after all). What I enjoyed about their talks were both their talk about real world examples, as well as important advice as well as the delivery. Getting the balance right is really difficult to do.

I also really liked the keynote from Dirk Duellmann from CERN who talked about the big data challenges they have storing information. Although it took a while to get to the meaty part of the data, storage details I think it’s a very interesting outlook they have with architectural choices such as the view that they cannot design for hardware or devices today as these will be obsolete as time goes forward. Being able to retrieve historical information is important as it the ability to store all of the data in a format others can read. They have realised the importance of the scale of the work they are doing, so they are focusing on doing something good (storing and making available data) and working with other groups to do the analysis.

There were loads of highlights such as meeting many new people and connecting with old ones as well as some interesting side conversations.

I gave my talk (above) and was very happy with the results. The Trifork team behind the conference are awesome at getting feedback to presenters for quickly and I was very happy with the results. The conference uses a simple voting system for feedback (red, yellow, green) and they keep track of the number of walk outs. I ended up with 90 green, 26 yellow, 1 red and only 2 walkouts. I have no idea how that compares with other speakers but I’m pretty happy with the results. What I also appreciated were the people who came up afterwards to talk to me about how the topic is really important and what some people got out of it (affirmation they are doing the right thing, new ideas to take back, new books to read, more things to focus on, or a good idea of how to prepare as they step into the role).

« Older posts

© 2024 patkua@work

Theme by Anders NorenUp ↑