28 November 2012

JavaScript Continous Testing Smackdown

I'm pretty new to serious JavaScript coding. By serious, I mean writing lots of pure JavaScript, as a contrary to putting some JavaScript functions into your <script> block to fizz things up.

I immediately sought out a way to get a smooth TDD development cycle running. I already had a theoretical idea on how to do this - from some of my colleagues who are JavaScript elitists in my book.

As far as I'm aware of, the technique described in this post is THE way to do test driven JavaScript development these days.

So, what will I get from this stuff?

You'll get a terminal which automatically runs your JavaScript tests immediately as you change code. Like Spork in Ruby On Rails, or Infinitest in Eclipse.

Consider the alternative. Having to refresh your browser on every change of JavaScript code. Sound familiar? It's no good; for your quality of life or your aging process (gray hair). Little less your productivity.

Under the hood, these guys passes your JavaScript code to "browser slaves" and shoves the test-results back into your terminal window. I've tried both Buster.JS and JsTestDriver. So, which is the king of TDD JavaScript (in my opinion ofcourse)?


Buster.JS
jsTestDriver using watchr































All the code from this blog entry can be found on github. Under a busterjs and jstd branch. https://github.com/finnjohnsen/jstesting


"But hey..? There are other ways to test JavaScript code"

I've noticed, and none I've found are attractive at all.

  • You can refresh your browser and see if your stuff works. You'll have to do this anyway in addition to your unit testing. But you certainly don't want this to be the only way to test your code. It often involved clicking and typing to reach the code you know you want to test. Horrible and unfortunately probably the most common way to do JavaScript development in the world today (I made that up, it's just my guts talking).
  • I've seen set-ups which involves refreshing or triggering the tests from your browser. For example at the Jasmine -site, this is what they document. This is a lot better than the previous bullet point. But we can do better.
  • The Selenium approach: Automate and play back clicks and input-text into your browser. These kind of behavior tests are tightly coupled to html, slow and messy. But they have value as they ensure your site actually works! But it's not what I want for unit testing.
  • Pure Node.js testing. I've tossed this idea, admittedly without really digging into it. Because browsers are too important. Different browsers have different JavaScript implementations, so isolating my tests to the Node.js v8 runtime feels fundamentally wrong.
If you know of other ways, please comment.



Buster.JS or JsTestDriver: My Conclusion


Both frameworks gives you a speedy feedback when working test driven development in JavaScript. However, either will help you all the way - as you may have wished.

  • jsTestDriver requires more patching, hacking and scripting, but works well once you've got it running. You'll probably polish and tune your scripts for quite some time after you've got it running, but you have something working pretty easily. JSTD has wide adaptation and you'll almost certainly find a solution to your problems just by searching around a little. You can have a look at this wrapper, which is suppose to ease the scripting from JsTestDriver. Anyway, check out my sample set up.
  • Buster.JS is a bigger creature. It seems to aim to provide you with some of the pieces missing in JsTestDriver. It provides contrinous/auto testing and an built-in assertion library. These two critical components are what made the installation and set-up of jsTestDriver take a while longer than buster. So check out my sample set up.
I spent about 45 minutes setting up this JSTD project, and 15 minutes on Buster.js. However I've done this before. I suspect I spent at least 2 or 3 times as much the first time.



Buster.JS states on it's website it is under heavy development. It even and uses the word 'unstable' to describe its current state. Leaving JSTD the choice if you can't live with that. Personally I like getting as much set up for me as possible, and I found nothing mentionable when setting up Buster.JS. So I will use Buster.JS on my next green field. I can however be considered biased, one of the authors of Buster.JS work in the same company as I.






This is nice and all, but the real world is bigger than this

... and nobody said it was going to be easy.




I found nothing in these projects which mentions, or in any significant way help you on how you should incorporate these para dimes into your real world web framework. So if you're using Rails, .NET or Grails, you're on your own hacking and slashing this stuff into your source code. You'll have a system within your system.

I also find it immensely difficult to test UI code. That is, code working with the DOM, utilizing templating like Handlebars.js or Mustache.js. And whenever I see jQuery I know I'm in trouble. It is unfair to blame these technologies for not helping you with this, so I won't. I'm just saying this is unsolved and up to you to mess about with on your own.

Personaly, my pure GUI code is not covered by any tests in my projects. I'm still searching for a sensible way to organize so I can test as much as I should. I'm looking for good coding standards on how to organize my code and .js -files. At the moment, I'm inventing stuff as best I can as I go along. I've typically got *-view.js which are untested, and contains the messy CSS selectors and stuff. And I have typical *-model.js which are the ones I test.

Continous integration is also something I've only tried for 15 minutes and given up. Maybe I'll blog about this when I solve it. You won't get this for free either.

I plan making a follow up blog entry covering an example (how I do it) on how to get this into a Grails project.

Get your hands dirty already!

Head over to https://github.com/finnjohnsen/jstesting and look at the simple skeleton set-up I made for the sake of this post. The project has a jstd and buster.js branch for you to look at.


Pros, Cons and anoyances

These are my raw notes when getting the simple skeleton set up.

JsTestDriver

Pros:
  • Created by Google and seems widely adopted and easy to search for questions, answers and sample setups.
  • Has a nice set-up video on the official site. http://www.youtube.com/watch?v=V4wYrR6t5gE
    • This video is gets eclipse specific when it comes to the automated testing.
Cons:
  • The web site is ugly and messy. I don't find it intuitive where to find what I need. Also the wiki linking to source often don't work.
  • You'll spend an hour setting this up even though the video made it seem easy. You'll have to create scripts which you'll tweak quite a bit over time before you're happy.
  • The project provides no proper continuous testing. I created a watchr script for this.
  • It was a little difficult finding the QUnit adapter or whatever assertion framework is supposed to be default. I blame the poor website for this.

bumps in the road:
  • new JavaScript files doesn't seem to be discovered. Maybe I'm doing it wrong? I always restart the server and watchr script. --reset flag?
  • Difficult to put the config file other places than in the root of the project. I gave up on this and ended up polluting my root with the config file.
  • The jsTestDriver.conf is case sensitive. Stole a few minutes to figure out I had incorrectly put it all in lower case.
  • Have to repeat the port number in the port file and the configuration file. I don't like having to remember stuff.
  • I had to create a dummy test.js to be able to start the server the first time.
  • The default assert library on the site directed me to a dead link, so I chose QUnit.
  • Can't figure out how to get colors in console. I miss the Test(s) failed or Test Successful which is convention everywhere you do TDD.
  • The projects provides an eclipse plugin. I couldn't get it working however. I gave up quickly though.

Buster.JS

Pros:
  • I got automatic relaunch of tests when files were touched. No need for watchr.
  • Got coloring in the console. Red and Green for failed and successful tests respectively.
  • The config file (buster.js) is easily put in test/ instead of root. Repeat after me: "I hate polluting root directory"
  • The web site is decent.
Cons:
  • I don't like having to mess with Node.js in my OS. Wish it was stand alone or just in my home folder 
Bumps in the road:
  • I couldnt get the expression test/*Test.js working. I prefer calling tests e.x personTest.js, not person-test.js. No biggie.
  • I intially had test/*.js in buster.js, but buster.js was in test/ as well, and I was trying to run buster.js as a test, which it isn't. Got weird errors which wasn't pointing me in the right direction.
  • I don't like having to run run the npm command as sudo. Unknown stuff leaking into my OS.

aaaaalright

This is a tight race, and there is no clear winner in my opinion. None will rescue you from the horrors of JavaScript web development, and none of them suck. I will prefer Buster.JS, strictly because I'm lazy and want everything out of the box. JsTestDriver has "Google" anchored into it's brand, making it a pretty safe bet also.

Thanks, feel free to comment :)
.finn

3 comments:

  1. I find testing pure UI stuff to be pretty easy with Backbone.js (but really, any well structured codebase would do, it's just that Backbone stronly encourages a sane structure).
    You just hand your view ("view" as in classic MVC pattern) an element, ask it to render and then check if the element looks right. There's really no need to attach it to a DOM to do this.
    If views are not coupled they can easily be tested in isolation; all they will ever ask for from you is a model and a div/ul/table/whatever. The test itself acts as your controller.

    ReplyDelete
    Replies
    1. So a abstraction layer between your View and the DOM?

      So your tests makes a ex. (fake) click on your View, and you assert that values in some model objects are in the right state?

      Delete
    2. When a click event or something similar happens in my view I am usually not testing what happens to the model if I am doing unit testing. I don't really feel the need to test that event-binding works as it's not intrinsic to my app. I know event binding works, so I'd rather have separate tests for my models.

      When I test a view component I usually check if it renders the way I want it to given a certain state in the model, or that it responds correctly to clicks when it comes to things like "If I click A then B changes color" or stuff like that. While my views do call methods on my models I'd rather test that those calls work in a test that focuses on the model.

      Testing the interactions between a view and a model is in my opinion integration testing and for that I'm totally fine with using selenium or something similar. I generally don't like to involve more than one "class" in my unit tests because I think it leads to tight coupling and brittle tests.

      If you want to test that a view calls methods on its model when certain DOM events happen then there are mock frameworks like Sinon around which can help with that. I personally don't like that kind of "unit tests" so I tend to avoid writing them.

      Delete