Talk about words you never thought you’d hear yourself saying!
If you’ve ever worked with me (or probably even spoken with me) you know I love Node.js. I love everything about it…Javascript, the frameworks, the community, everything. I’m especially fond of the trend toward NoSQL data stores in combination with Node.
Having used one or more NoSQL databases in my last few projects, I know they’re fast, usually Javascript based, and the absolute wrong choice for a project I just started. The right choice: a relational database. MySQL being my choice.
I’m not going to go into why that’s the right choice, or MySQL over PostgreSQL, or InnoDB over MyISAM. There are a number of reasons, but it mostly comes down to the project requirements and personal preference.
FTW!
What does it matter? Isn’t the database independent of the scripting language?
Yes and no. They’re independent systems, but we still need drivers to interface between them. That’s where the problem lies.
With the focus on NoSQL data stores, I don’t think anyone has taken the time to write a performant MySQL driver. The situation gets even worse with ORMs and worse still with newer versions of Node. Most of the solutions I found don’t support Node 0.7.x.
Wanting desperately to “make it work,” I spent some time this weekend comparing different combinations of Node drivers and frameworks/ORMs with those available in PHP. PHP is the clear winner.
If you’re wondering how I arrived at those results, the repo is on GitHub. Feel free to fork it f you have another driver or ORM you think should be included.
The Point
Pick the right tool for the right job. Look at the results above. The right tool for MySQL is not Node. Not yet anyway.
Who doesn’t love head? I do. Whether or not you do, it’s an important component of our testing process. In my last post we set up our linting and used BusterJS to TDD our HTML in an instance of PhantomJS, a headless browser. Unfortunately, the real world isn’t all sunshine and webkit. That’s why we need to test in real, GUI-ful browsers with all their quirks and differences…yes…head. You know you love it!
On a decently fast computer, the speed difference between running your tests in PhantomJS alone and also running them in IE, FF, and Chrome is negligible. Due to the forethought of the developers, we can actually test all of our browsers with the current setup. Once our buster server is running (see Part 1) we can connect all the clients we’d like to test to localhost:1111. In my relentless pursuit of automation I use the script below to automate opening the browsers and connecting to the Buster server (OSX only for now, sorry).
#!/usr/bin/env bash
open -a 'firefox' 'http://localhost:1111/capture'
open -a 'opera' 'http://localhost:1111/capture'
#using PhatomJS so I don't run these on every test
open -a 'google chrome' 'http://localhost:1111/capture'
open -a 'safari' 'http://localhost:1111/capture'
Very simply, this script opens my browsers and navigates them to http://localhost:1111/capture. The /capture endpoint is simply a shortcut for clicking the “Capture Browser” button – this way the browsers open and immediately start listening for tests. I also use VirtualBox with IE9 on a Windows 7 VM which I have to start manually and connect via IP address. Leave it to IE to be the biggest pain of them all!
spacer
spacer Personal Note: I’m an NPM junkie so I use the package.json scripts to start the Buster server, PhantomJS, and my browsers. This way, everything is setup at once with a quick “npm start“. See the package.json in the Github repo if you’re interested in a similar setup. spacer
Consolidate Our Various Tests
Ok, we know we need to lint our code, we know we need to test our code, and we know we need to do this a hundred+ times a day. What we need is one command to do both so we can UP-Enter or setup our IDE to run it for us. If you’re looking at the Github repo then you’ll find it in package.json for easy execution with “npm test“, but if you didn’t…things are about to get real! Here’s the script:
#!/usr/bin/env bash
jake lint
buster test
Whew! Did you get all that? I’m exhausted. j/k. Looks simple enough, right?
That works, but by the same thought process that we relentlessly refactor our code after testing, I’m gonna consolidate our configs a little bit. After adding the tddhtml5-html-lint and tddhtml5-html-buster repos I copied all the files into the root directory so I could run the script above. This leaves us with buster.js and config.js which essentially do the same thing: hold config data. Since Buster is particular in how the config is retrieved I had to leave that file, but we can move the Buster config into config.js and just import the config into buster.js. Now we have one file with all of our testing configs.
buster.js
var config = require(__dirname + '/config');
module.exports = config.buster;
spacer
In an effort to keep my posts shorter than usual, I’m going to stop there spacer
Question About Testing Tools
Prior to hearing about BusterJS a month or so ago, I used JsTestDriver quite extensively. Are there any other test frameworks/harnesses that make frontend testing easy? I’ll talk about Selenium and browser automation in a later post, but I don’t consider that TDD. It takes a while to run so you generally have to write your implementation first to save the time of watching the tests fail.
I’ve been racking my brain for a week straight on the best way to employ test-driven principles to HTML. Hell, I’ve been trying to figure out if it even can be done. Trying to figure out if it’s even worth it. Mostly through blind faith, I’ve come up with the beginning of what I hope becomes a winning strategy for test-driven HTML development.
“…the beginning of what I hope becomes a winning strategy…” – lol, how’s that for confidence
What Exactly Are We Testing?
The look and feel of the UI is handled through CSS. If you’re still using tables for layout then there may be more value here for you, but if that’s the case, seriously, stop reading this right now and catch up on the last 15 years of web design. We’ll be waiting. Ok, back to 2012. If the “look and feel” are handled by CSS and the fancy stuff is handled by Javascript (e.g. AJAX, client-side form validation, image sliders, etc), then what’s left to test?
Do your links go where they’re suposed to? – because you’re dedicated to unobtrusive Javascript, all your links have an HREF attribute, right?
Do your view templates render their content into the right DIV?
Do your forms redirect appropriately when submitted? – or not redirect as the case may be
Do portions of your home page fail to load when you change the JSON output of a seemingly unrelated API endpoint (that happened to me)
Most importantly, can you refactor your views and layouts with confidence?
If you can already answer “yes” to the questions above, then skip testing your HTML. It’s not necessary. I don’t say that rhetorically either; it’s possible to manually test your site in such a way that you can answer “yes” to everything on that list. My guess is you are the minority, however. Personally, I hate debugging. It’s time consuming, and it’s usually a bug caused by a missing semicolon or something stupid. I want to spend my time solving problems, not manually linting code. Hence my obsession with testing. I’ve saved countless hours over the past few months due to well thought out, well written, and automated tests. Now you can too.
Linting
Linting is a given, or at least it should be. Make sure your code is clean. Again, this can be done manually by copy/pasting your HTML into one of the dozens of HTML linting websites and clicking “Go”. For the ambitiously lazy people like myself, we automate. I use Perl’s HTML::Lint (easily installed using CPAN) in a simple script that I run with a Jakefile. In my implementation there are a lot of config options and such that I’m leaving out for brevity; follow the GitHub repo and I’ll eventually update it with my full code.
Perl Script:
#!/usr/bin/env perl
use strict;
use HTML::Lint;
if (@ARGV == 0) { die("You must provide a file to lint.\n"); }
my $linter = HTML::Lint->new;
my $numerrors = 0;
foreach my $arg (@ARGV) {
$linter->parse_file($arg) or die("Cannot lint file: ", $arg, "\n");
if ($linter->errors > 0) {
$numerrors += scalar($linter->errors);
foreach my $error ($linter->errors) {
print $arg, ": ", $error->errtext(), " at ", $error->where(), "\n";
}
} else {
print $arg, " ok.\n";
}
}
if($numerrors > 0) {
print "-----------------\n";
print "Total Errors: ", $numerrors, "\n";
exit 1;
} else {
print "-----------------\n";
print "No errors found.\n";
exit 0;
}
Jakefile:
(function() {
'use strict';
task('default', ['lint']);
desc('Lint our layouts, views, and templates');
task('lint', function() {
//get the correct files for linting
var files = new jake.FileList();
//including all 3 lines below may be overkill,
// but it helps illustrate some options
files.include('index.html');
files.include('src/*.html');
files.include('src/**/*.html');
//don`t lint our tests
files.exclude('test');
var fileNames = files.toArray().join(' ');
var perlLintCmds = [ 'perl ./html_lint.pl ' + fileNames];
var exec = jake.createExec(perlLintCmds, { stdout: true });
exec.addListener('cmdEnd', function() {
//since we only execute 1 command we don't need to count the number
// of commands to determine when we're done. just finish.
complete();
});
exec.addListener('error', function(msg, code) {
//just add a blank space to make the output more readable
console.log('');
//fail the Jake test
fail(msg, code);
complete();
});
exec.run();
}, {async: true});
}());
Run the Linter:
$ jake lint
Semantics And Document Structure
I haven’t seen anything to test for semantically correct HTML as this would require someone to interpret the meaning of the content of your tags (highly improbable and subject to interpretation). There may be opportunity in building a markup language that expresses the intent of your content and creates the appropriate HTML tag for its components. I envision something similar to jade, except instead of tag names preceding a line, it would be an intention descriptor. Fun to think about, but not something I’m passionate enough about to dedicate any significant time to. If you decide to write one though, let me know. I’d love to contribute.
Basic HTML5 Document Structure
Testing the document structure seems kind of pointless with so many people using jade, ejs, haml, etc. I was working on a YAML interpretation of document structure that, combined with jQuery, would load a page and test the structure was correct. the YAML ended up looking a lot like jade though so I dropped it. It felt like I was trying to employ test-driven principles to writing copy…that’s going a little overboard IMHO.
CSS tests catch most anything relating to the UI anyways (more on testing CSS to come). If your CSS tests are solid then anything the user actually cares about will be tested by a combination of CSS and functional testing. Which brings use to our next topic…
Functional Testing
Now that we can lint our code with easy, we can start on functional testing. Functional testing is where I see the most value in testing HTML. So, how do we write tests first to keep our code lean and mean? I recommend a 2-step approach. Headless browser tests first for the sake of speed, then in-browser tests for compatibility. If you don’t know what a headless browser is or the pros and cons of using one, please Google it real quick. I’m a huge fan of almost everything Learnboost and TJ Holowaychuk have put out for the community, so I used Tobi for a long time and I loved it, but it’s so out of date now that it’s no longer useful .
For this project, I looked at Zombie, but they really want you to run your app and test the whole stack. Since this is unit testing, we’re looking to test the smallest portion of HTML that we can. That’s definitely not the full stack. After some research, I’ve decided to give Buster.JS a shot despite being in beta still. It’s actively being developed by Christian Johansen who literally wrote the book on Javascript TDD so I’m pretty confident it’ll work for our simple HTML tests.
Enough talk. How do we get rolling with test-driven HTML? First install NodeJS, then create a directory and install BusterJS via NPM and download the PhantomJS binary for your system and extract it to ./lib/phantomjs. Automatic headless testing is coming to Buster, but it’s not quite there yet. In the meantime, they’ve provided us with a handy script to configure PhantomJS for use with Buster.
Execute the following to start the Buster test server and an instance of PhantomJS with the correct settings.
buster server &
./lib/phantomjs/bin/phantomjs ./node_modules/buster/script/phantom.js &
Great, now for the Buster config file. (I swear this is the last step before actually writing some tests). Also, I downloaded jQuery and placed it in the ./lib folder because it makes DOM stuff really easy so we can concentrate on writing solid tests. If you’re not using jQuery in your project, use whatever library your project is built around to ensure compatibility.
buster.js
var fs = require('fs');
var config = module.exports;
config["TDD HTML Headless"] = {
environment: "browser",
libs: [
//this just brings in jQuery
"lib/*.js"
],
tests: [
//our test file (yet to be created)
"*.test.js"
],
//allows us to bring in our views without firing up a server
//@see: http://www.kraken.no/blog/2012/03/23/injecting-html-with-busterjs/
resources: [ { path: "/form", content: fs.readFileSync('form.html') } ]
};
Finally, on to the good stuff. We’ll write our test first of course.
form.test.js
buster.testCase('Login Form', {
setUp: function(done) {
var self = this;
//this calls the "/form" route we set up in the "resources" section
// of our config file above and reads in the HTML from "form.html"
$('body').load(buster.env.contextPath + '/form', function() {
//use jQuery to grab our form element
self.$form = $('form#login');
done();
});
},
//the simple tests are always the best, make sure the form exists
'exists': function() {
//since jQuery represents selectors as arrays, this will always exist, so
// we'll test the length to make sure there's an element in the selector
assert(this.$form.length);
}
});
To run our test, we type
$ buster test
…and we get something that looks like this…
PhantomJS 1.6.1, OS X: F
Failure: PhantomJS 1.6.1, OS X Login Form exists
[assert] Expected 0 to be truthy
Great! It failed. Tests should always fail when you write them. Now, the HTML to fill our test.
form.html
<form id="login"></form>
Some more tests
'method is POST': function() {
assert.equals('post', this.$form.attr('method').toLowerCase());
},
'action is /login.php': function() {
assert.equals('/login', this.$form.attr('action'));
},
'has a username input first': function() {
var $usernameInput = this.$form.children(':first');
assert.equals('INPUT', $usernameInput.get(0).tagName);
assert.equals('username', $usernameInput.attr('name'));
}
…repeat until the view is complete. Remember to test the actual submission of the form, client-side validation if it adds elements to the DOM, server error handling, etc. Check out SinonJS for stubbing server requests. It’s written by one of the authors of Buster so it has really nice integration. Trust me, you’ll have the best night’s sleep you’ve had in a while. Now you’re confident that the form works when it’s loaded into a layout, or several layouts as is often the case with login forms. What happens if it doesn’t work when it’s inserted into a layout? Well, we’ve eliminated (or significantly mitigated) the possibility that it’s a problem with the form, so it must be a conflict with something else on the page. For that we turn to full stack testing, which is a topic for another day. I’m thinking live browser testing should be too.
Next time…
Yep, this post has gotten pretty long already so I’ll go into setting up and running live browser tests in my next post.
Have I missed anything in testing HTML that I should consider testing? Is there a better methodology, framework, or tool that I should be using? Let me know your thoughts in the comments below or on Twitter.
I don’t want to get into a debate about Globals and Singletons right now. Suffice to say that I use Singletons on rare occasion and after creating one for a NodeJS project, I figured I’d save someone else the hassle. There are a bunch of answers on StackOverflow and one was very helpful…until I added 2 little words to the top of my constructor: “use strict”; (I’ve updated the answer on StackOverflow to reflect the changes I’m going to explain here, and it’s awaiting peer review).
I’m something of a rebel so I chose not to use a getInstance() method. I just don’t like the way it looks. If I want a reference to the singleton I should just be able to type:
var singletonInstance = Singleton();
var singletonInstance = new Singleton();
//...or similar
I started with this solution and it worked really nicely for a while.
function MySingletonClass() {
if ( arguments.callee._singletonInstance )
return arguments.callee._singletonInstance;
arguments.callee._singletonInstance = this;
this._client = function() {
// ...
}
}
Unfortunately, I want to use the most up-to-date methodologies if I can and right now that’s strict mode in Javascript. No problem, I just add “use strict”; at the top of my constructor and run my tests. They all pass. Great! Then I ran the full build, which includes JSHint, and the results were not so pretty. arguments.callee was deprecated in strict mode so I implemented the strict mode version of the answer on StackOverflow to get this:
I fired up node and all hell broke loose. TypeError: Cannot set property ‘_client’ of undefined. What!?! All my tests are green. JSHint is green. There can’t possibly be anything wrong with my code. I wish TDD meant that your code was perfect, but in fact it’s just for regression prevention. I didn’t have a regression, I had a whole new problem. If you instantiate a Javascript constructor in strict mode without the new operator “this” will be undefined. I eventually ended up with the following test and solution:
test('should not throw if created without "new" operator', function() {
(function() {
var instance = require('./singleton');
}).should.not.throw();
});
var MySingletonClass = function() {
"use strict";
if ( MySingletonClass.prototype._singletonInstance ) {
return MySingletonClass.prototype._singletonInstance;
}
var self = this;
if (typeof self === 'undefined') {
self = new MySingletonClass();
}
MySingletonClass.prototype._singletonInstance = self;
self._client = function() {
// ...
};
};
I hope that someone is saved the headaches that I was by using the above. It’s literally copy, paste, change the singleton name, and you’re off and running. Let me know if you have any comments/improvements.
I work extensively with Javascript on a daily basis and I often find myself Googling best practices, tips, tricks, etc. Over and over I come across articles proclaiming the horrors to befall you, your loved ones, and your website’s performance should you even consider using document.write(). I’m fairly certain at this point that there’s a law somewhere stating I should be taken out and stoned for suggesting that document.write() may, in fact, be ok to use in some situations. Let’s just hope the authorities don’t read this, eh? Read more