Alexander Beletsky's development blog

My profession is engineering

Building Single Page Applications with Backbone.js and Express.js

This Saturday I was doing my first Backbone.js class. It went really fine, except one fact.

I wanted to show the aspects of creating Single Pages Application running on Backbone. Started the application from scratch, we had to spend a lot of time writing some infrastructure code before we even jump to Backbone. So, after the class I decided to create some boilerplate project, so next time we would just clone it from github and start to work.

.NET Developer in JavaScript World

This is a subscript of lighting talk I did on #odessajs conference that took place 13 April in Odessa.

The story

Thinking about the things going on inside and around me I realized - I don’t do .NET development more than 1.5 years for now. It’s not the first time I was thinking about that, but now it has a bit different perspective.

WTH?

I work in E-conomic company and about 2 years ago we decided to build new product.

Till that time we’ve learned hard few things: plain jQuery applications is mess, building new shinny product on out-dated infrastructure is bad idea.

So, we’ve started with front-end by choosing Backbone.js as our foundation, later on we’ve decided to switch our API server from .NET to Node.js.

It appears we got full JavaScript stack on that product and I involved in JavaScript more and more each day.

Likeastore, Application Built on Hackathone

It’s my second time I attended hackatone. As last time, it took place in Ciklum, the best office in Kiev, so I had very strong wish to visit it. My previous experience showed several things: hacktone is absolutely unpredicable thing, you can’t guess which projects will be popular or not.. and the second, that it’s hard to do something valuable alone.

So, this time I decided to build a team of guys I know and trust, so we can do something together. Those two guys appeared to be my colleagues, in @debitoor project @voronianski and @mamant. We gathered together, to build a product which was called - “Likeastore”

Baby steps to Backbone.js: Exploring collections

After initial introduction to Backbone’s views and models, we going to look on next Backbone.js fundamental entity - Collection. Collections represents the ordered set of models and became very handy for any type of applications. Consider that, we almost always operate with set of different models: posts, tweets, news etc. all of that are collections, typically rendered as lists or grids.

Looking back to 2012

It’s the first Saturday of New Year, so it would be good idea to spend some time thinking of year that passed.

Work in e-conomic

That was yet another great year in e-conomic. We have build great app - Debitoor. Along the way, we were trying different approaches, re-writing things from scratch, designing and re-designing again and again. Finally we got very solid application built upon Node.js, MongoDB and Backbone.js. Debitoor has been created with scalability in mind, we started with Heroku cloud service, eventually moved to Amazon EC for back-end and Amazon Cloud Front for front-end. Now, it’s working amazingly fast and for 8 month we gathered ~20,000 signups, launched service for many countries.

Baby steps to Backbone.js: Unit testing of views

Previous time, we’ve implemented a Backbone model and wrote some meaningful tests for it.

Next very important Backbone’s entry to test is View.

Views are central concept in framework. Views are actually ones who do stuff. We don’t have Controller in Backbone, but according to Controller initial idea as user input handler, Backbone views following exactly the same architectural goal, so they could be treaded as controllers in some way.

What to test?

You should focus on such things:

  • Initialization - test that view is provided with all required inputs like: model or collection, localization texts, different options. If view is not able, say, render without some option, you should test that exception is thrown.
  • Rendering - test that required html appeared in view. I do not create those very strict, since they would be to fragile in case of markup changes, but still I check for major DOM elements are present and have right styles.
  • Events - test that view is correctly handle DOM events.
  • Model changes and persistence - test that changes in view are correctly propagated in model. If view is about to persist the model, that could tested as well.

I see tests of views as kind of integration test. You communicate to DOM, listening to events, updating models or changing DOM elements state. It’s typically that view tests are catching most regression issues.

How to test?

Fortunately, the Backbone views are designed in very testable way. You don’t need any special HTML on test page, since view holding all it’s DOM structure inside this.el. By means of jQuery, it’s easy to change DOM values or trigger events.

Since view also holds reference to model, it’s easy to check model’s attributes changes or spying on particular models methods.

First red test,

describe('FeedbackFormView.js spec', function () {
    var view, model;

    beforeEach(function () {
        view = new FeedbackFormView();
    });

    describe('when view is constructing', function () {

        it ('should exist', function () {
            expect(view).toBeDefined();
        });

    });
    

Here we basically testing, that FeedbackFormView should exist, so as soon as function is in place, test should be green.

FeedbackFormView could not exist without a model as well as default feedback text, that should be used as initial value of textbox. In the language of TDD, that means:

describe('when view is initialized', function () {

    describe('without model', function () {

        it('should throw exception', function () {
            expect(function () {
                new FeedbackFormView();
            }).toThrow(new Error('model is required'));
        });

    });

    describe('without default feedback', function () {
            
        it('should throw exception', function () {
            expect(function () {
                new FeedbackFormView({model: new Backbone.Model() });
            }).toThrow(new Error('feedback is required'));
        });
    });

});
    

To turn from Red to Green, let’s add initialize function to view,

initialize: function (options) {

    if (!this.model) {
        throw new Error('model is required');
    }

    if (!(options && options.feedback)) {
        throw new Error('feedback is required');
    }

    this.feedback = options.feedback;
},
    

Btw, after that change it’s required to fix previous test, since it start to fail.

describe('FeedbackFormView.js spec', function () {
    var view, model;

    beforeEach(function () {
        view = new FeedbackFormView({model: new Feedback(), feedback: 'TDD is awesome..' });
    });

    describe('when view is constructing', function () {

        it ('should exist', function () {
            expect(view).toBeDefined();
        });

    });
    

Now, let’s test how rendering works.

describe('when view is rendered', function () {

    beforeEach(function () {
        view.render();
    });

    it ('should email field be empty', function () {
        expect(view.$el.find('input#email')).toHaveValue('');
    });

    it ('should website field be empty', function () {
        expect(view.$el.find('input#website')).toHaveValue('');
    });

    it ('should feedback field with default feedback', function () {
        expect(view.$el.find('textarea#feedback')).toHaveValue('TDD is awesome..');
    });

});

Here and after I’m using very nice Jasmine plugin, called jasmine-jquery. It adds a number of matchers, very useful for testing jQuery objects.

We’ve tested initialization and rendering, now let’s test last aspect, model changes and persistence.

Suppose a user inputs nothing, erases default feedback and presses submit button. Expected behavior is expressed with this test:

describe('when form is submitted', function () {

    describe('no inputs are filled', function () {

        beforeEach(function () {
            view.$el.find('#email').val('').trigger('change');
            view.$el.find('#feedback').val('').trigger('change');
        });

        beforeEach(function () {
            view.$el.find('#submit').trigger('click');
        });

        it('email field should be invalidated', function () {
            expect(view.$el.find('.control-group.email')).toHaveClass('error');
        });

        it('feedback field should be invalidated', function () {
            expect(view.$el.find('.control-group.feedback')).toHaveClass('error');
        });

        it('website field should be valid', function () {
            expect(view.$el.find('.control-group.website')).not.toHaveClass('error');
        });

    });

That’s cool. After those tests are passing, we can test that if only email is filled, but feedback is still empty, we are not able to submit the form. By analogy of previous test,

describe('only email field filled', function () {

    beforeEach(function () {
        view.$el.find('#email').val('a@a.com').trigger('change');
        view.$el.find('#feedback').val('').trigger('change');
    });

    beforeEach(function () {
        view.$el.find('#submit').trigger('click');
    });

    it('email field should be valid', function () {
        expect(view.$el.find('.control-group.email')).not.toHaveClass('error');
    });

    it('feedback field should be invalidated', function () {
        expect(view.$el.find('.control-group.feedback')).toHaveClass('error');
    });

    it('website field should be valid', function () {
        expect(view.$el.find('.control-group.website')).not.toHaveClass('error');
    });

});

And finally, if view is filled correctly,

describe('email and feedback filled', function () {

    beforeEach(function () {
        spyOn(view.model, 'save').andCallThrough();
    });

    beforeEach(function () {
        view.$el.find('#email').val('a@a.com').trigger('change');
        view.$el.find('#feedback').val('some feedback').trigger('change');
    });

    beforeEach(function () {
        view.$el.find('#submit').trigger('click');
    });

    it('should show no errors', function () {
        expect(view.$el.find('.error').length).toBe(0);
    });

    it('should save model', function () {
        expect(view.model.save).toHaveBeenCalled();
    });

});

Here, we test 2 things: first, that no validation errors appeared on form and second that save method of model is called. Jasmine built-in spy framework is used here. You can setup on any function of any object and then verify that function has (or has not) been called.

I’m skipping the implementation of that view, but you can find the code of application in gist.

Conclusions

While you typically start with defining and testing models in your application, view is integration test that helps to test actual behavior of application, depending on model states and events. You should define how “deep” you want to test the view, but my proposal is to focus on initialization, rendering, changes (validation) and persistence.

Retrospective of Trackyt.net or Best Project I Ever Accomplished

It’s been to years ago, I’ve released my own project - Trackyt.net. Month ago I’ve cancelled my VPS lease contract, so your would not see it available at http://trackyt.net anymore.

This project is first attempt to release something. I had some pet projects before, but never ever made them done. Trackyt.net started as simple application to learn some JavaScript programming and HTML. It turns out to be much, much, much more. Even it seems to be very simple, but I’ve spent a lot of energy on it.




The product did not gather any meaningful amount of users neither good amount of traffic. For almost 2 years I was investing money/time on it. So, from the first sight it might appear as failed project.

Any kind of business should be evaluated in terms of assets. It’s not necessarily means money. Or more correctly, it does not meant “direct” money. I had no paying customers and I actually never thought to sell the product. But, I can prove it - every hour I spent on the project got back to me as value. And the most valuable asset for developer is skill, of cause.




I’ll try to analyze what actually happened:

  • Web development and design: even my professional involvement into web applications, did not give me a feeling of web development. I blame ASP.NET Web Forms for that, but I realized that I lack that kind of skill. That was my first project were I did everything by myself, all HTML/CSS/JS were handmade, not stupid WebControls. My eyes opened on how many things I did not understand, how difficult the front-end might be. Crafting of UI is a huge pleasure for me, I simply like beautiful things. 2 years after I will never call Trackyt.net beautiful, but it was a great point to start.

    Outcome: I overstepped the uncertainty and fears of front-end. During the JavaScript programming. I’ve seen a lot of value in front-end MVC frameworks, so I had no doubts of moving from jQuery based apps to Backbone.js (or other MV*) frameworks. I did a several speeches of JavaScript applications development and got really cool feedback on it.

  • Test driven development: practicing TDD for quite long years already, so Trackyt.net was not my first project were I applied the technique. The difference was that usually the we work with some legacy code at work, so TDD could be difficult to apply. Starting that project I had strong opinion on TDD and literally TDD’ed everything. UI, Controllers, Repositories.. JavaScript and C#. That was so great experience, I never felt on any other projects. During Trackyt.net I think I moved from Shu to more clearly see what and when should I test to stay in good shape of application.
  • Outcome: currently I’m professional TDD trainer at XP Injection and Trackyt.net much influenced that it happened. I share my experience with guys I show some examples of code there. I wrote a bunch of blog posts on TDD and it’s Trackyt.net who gave most inputs and insights. I’ve implemented integration tests for my API based FuncUnit and that blog post became the part of JavaScript Weekly and still remains one of the best reads in my blog. I’ve learned UI testing and formed my opinion on that.

  • ASP.NET MVC: Never used ASP.NET MVC before, I armed with great book by Steven Sanderson I’ve started to learn new framework. And Trackyt.net was main playground. Starting up with MVC2 and lately ported it to MVC3 I had a chance to play different aspects of framework. ASP.NET MVC was my first serious step into web development world.
  • Outcome: I became a huge fan of this technology, learning it from deep and posting information on it. I had a few speeches about ASP.NET MVC.. and the biggest one on MS SWIT 2012, there ~300 persons were listened to me. Then the company I worked to were deciding about back-end technology for API of new product I helped to integrate ASP.NET MVC into existing web site and we successfully built API on it. I assisted my team mates to understand the framework, it was not difficult at all, since I knew the stuff. Currently I have a training course on ASP.NET MVC as well and already conducted some.

  • API oriented architecture: popularized by Twitter and GMail, the architecture and having RESTfull JSON based service behind and the JavaScript-driven application on front-end, was my high-level architecture for Trackyt.net.

    Outcome: I successfully used the same ideas on other projects and it worked really well. For now I’m consulting few companies that tries to adopt same ideas. The most popular post in my blog is about build REST(like) services with ASP.NET MVC.

  • Continuous Delivery: being annoyed by manual update of production environment I’ve built simple continuous delivery pipeline that worked really great for me. During the way I’ve met such great tools as Jenkins, UppercuT, RoundhousE etc. That was my first try of this approach and I felt huge power and advantages of Continuous Delivery. Slightly it became one of the professional areas of interests for me.
  • Outcome: Continuous Delivery and Chuck Norris tools was the topic of my first talk on KievALT.NET. Since then I became an active participant of community and I really like it. Also, I’m talking about things like RoundhousE on my other speeches and trainings and I got a lot of pleasure than people stand by saying: “You know, we’ve tried that and it worked so nice, thanks a lot”.

  • Code base: the code base of application is rather small. There is C#, JavaScript, SQL and some NAnt scripts. A lot of things I used there I still copy and paste (or at least taking a look) from Trackyt.net to my current projects. It became a kind of cook book, from there I could quickly pick up some recipe.
  • Outcome: I didn’t thought above that initially, but Trackyt.net codebase also helping me to see what improvements I’ve made so far. For instance, I reviewed the API controllers recently and realized that I would never write anything like that. Having some code that you produced year or two ago you peer and compare gives some really interesting insights. Besides of application code itself, I’ve made an C# API adapter and the most significant outcome is ELMAH.MVC.

Concluding this, I would say - Trackyt.net changed the things radically. I’ve became a speaker on KievALT.NET and UADevClub communities, my blog received an MVB status, I improved my skills and knowledge and I do trainings and consulting. Basically, I’ve “rebooted” developer inside me and saw my way of constant learning and improving.

Looking back I definitely say - it was only possible because of Trackyt.net.

Without any doubts, that was the most successful project to me!