Smashinglabs

Sebastian Poręba's blog

Unit testing for jQuery plugins – part 2

In part 1 I described some basic concepts behind unit testing in JS. Now it’s time to focus on CSS. There are few testing frameworks dedicated for frontend that are worth mentioning.

  • Selenium – well known testing utility, records your actions (in Firefox) and runs tests in different browsers
  • CSSUnit – there are several projects named CSSUnit, this one is checking if css rules are not overwritten or changed during development
  • There are also several projects like Watir which are testing utilities for Ruby, Python, Java and other exotic* languages
* exotic = not PHP. I hope it will change soon.
Selenium is very good for UI testing and if you are serious about your application, you probably should use (or at least check) it. I’m going to stick to QUnit in this article, as it can tests both JS and CSS in one run.

Basics

Very basic test would check if css is not changed. It’s actually what mentioned CSSUnit does.

  test('headers h2', function() {
   $('#headers h2').each(function() {
      equal($(this).css('font-size'), "12px");
   });
  });

or even better, counting and marking all errors:

  test('headers h2 counting', function() {
   var count = 0, elem;
   $('#headers h2').each(function() {
      elem = $(this);
      if(elem.css('font-size') != "12px") {
          count++;
          elem.css('border', '1px #f00 dotted'); // mark error
      }
   });
   equal(count, 0, 'There are ' + count + ' wrong headers');
  });

Notice, that second test will run only one assertion for one CSS rule, which is a quite good idea.

Box model (widgets)

One of the best use cases for CSS unit tests are widgets. If your project is full of these, you probably work on one, review it with your PM/customer and move to another. Complex sites with a lot of widgets tend to have enormous amount of CSS rules (for example, I work on one having 3k+ lines of CSS so far). Breaking something that was done few months earlier becomes a very common problem.

My approach is listing what we expect from widget to look like, f.e.:

  • header should have one line of text only (and it breaks with more)
  • width should be fixed 250px
  • font size should be 14px for header and 11px for text
  • inner image should have 5px margin from text
And turning it into unit tests:
  test('widget', function() {
    var widget = $('#widget');
    var header = $('h2', widget);
    var img = $('img', widget);

    equal(widget.css('font-size'), "14px");
    equal(widget.css('padding-left'), "5px");
    equal(widget.css('padding-right'), "5px");

    var padding_left = widget.css('padding-left');
    padding_left = padding_left.substring(0,padding_left.length - 2);

    var padding_right = widget.css('padding-right');
    padding_right = padding_right.substring(0,padding_right.length - 2);

    var widget_width = parseInt(widget.width()) + parseInt(padding_left) + parseInt(padding_right);

    equal(widget_width, 250);

    equal(header.css('font-size'), "16px");
    ok(header[0].offsetHeight < 30);

    equal(widget.offset().left + widget_width - (img.offset().left + img.width()), 5);
    console.log(widget.offset(), img.offset());
    equal(img.css('margin-left'), "5px");
  });

As you may see, I use jQuery.offset(), jQuery.css() and element[0].offsetHeight a lot. These are general rules I’ve found useful:

  • some .css() queries return string with ‘px’ suffix, you have to remove it with something like “padding_right.substring(0,padding_right.length – 2)”
  • it’s always better to use native offsets than read values from CSS. You can find values like ‘normal’, ‘20%’, ‘inherit’ in CSS and these are useless
  • Remember about box model. Real width is width + padding. Horizontal distance from another element is sum of margins and vertical distance is the greater of margins.

Floats

Last topic I want to cover in this part are floats. For Zark’s sake, please always check if your floating blocks have some clearfix.

The best way of testing this issue would be checking if child elements of every div have sum of height equal to their parent. Unfortunately it would work only with blocks with height: 100%, which is exactly what we don’t have when using floats. To check it the way it should be done, you would have to reimplement browser css parsing capabilities, calculate where every element should be placed and get correct expected height value. I know a guy who has something to say about that:

Instead of doing crazy shit like that, I recommend a simple test (similar to first h2 check):

  test('floats', function() {
   var count = 0, elem;
   $('div').not('.clearfix').each(function() {
      elem = $(this);
      if(elem.height() == 0) {
          count++;
          elem.css('border', '1px #f00 dotted'); // mark error
      }
   });
   equal(count, 0, 'There are ' + count + ' divs with 0 height');
  });

Test is a result of simple observation, that badly floated divs have always 0 height.

That’s all for part 2, I hope you liked it. You can grab the source on github. You will probably notice, that some of tests fail – it’s on purpose. Usually the explanation is in the article.

In part 3 we will finally get to actual jQuery plugins testing, so far it was only a warmup.


  • RSS
  • Facebook
  • Twitter

FAQ about Wordpress

This came as a surprise for me but gMap is ...

gMap 3.3.3 released

It was a looong time since I last visited gMap. ...

Talks for Google Dev

Two new slide decks appeared in lectures tab. This time with ...

Talks and lectures w

Every now and then I spend a weekend watching various ...

3D Tetris with Three

In the fifth part of tutorial we add some final ...

FAQ about Wordpress

This came as a surprise for me but gMap is ...

gMap 3.3.0 released

Christmas came early! New version of gMap is ready!

Lecture for GTUG: Ja

Today I gave a lecture for GTUG Krakow about optimizations in ...

Unit testing for jQu

In part 1 I described basics of unit testing in ...

Unit testing for jQu

In part 1 I described some basic concepts behind unit ...