Mats Bryntse
25 January 2013

Re-blog: Squash Variable Leaks with Siesta

This is a re-blog of a great recent blog post by Brian Moeskau at Extensible. JavaScript makes it incredibly easy […]

This is a re-blog of a great recent blog post by Brian Moeskau at Extensible.

JavaScript makes it incredibly easy to create global variables accidentally, which is inefficient and can also lead to very hard-to-debug bugs later. Simply omit the var keyword before a variable assignment, and you’ve inadvertently leaked a variable by defining it in the window scope.

Detecting Leaks with Siesta

Siesta, an awesome general-purpose unit and UI testing tool, includes a handy feature that allows you to automatically check for unexpected globals added during test runs. When setting up your test harness, you simply enable monitoring and define any expected globals like so:

[crayon striped=”false” lang=”javascript” nums=”false” toolbar=”false”]
Harness.configure({

autoCheckGlobals: true,
expectedGlobals: [‘Ext’, ‘Extensible’]
});
[/crayon]

If anything unexpected shows up in global scope during testing it gets flagged as a test failure — awesome!

However, tracking down the offending code can sometimes be tricky. The simple approach is to search in your project for instances of variable assignment. If you are leaking a variable named foo, a search on “foo =” will likely locate the problem code pretty quickly. In my case, I was leaking a variable named el, which is only assigned about a bazillion times in Extensible. My initial search returned a ton of matches, so it was time to think of something quicker.

Locating any Leak Instantly

A cool trick you can do in JavaScript is to override getter and setter functions (in modern browsers at least, I’m using Chrome). Thinking about it, adding a global is really the same thing as “setting a property” on the built-in window object, so I figured you should be able to do something like this:

[crayon striped=”false” lang=”javascript” nums=”false” toolbar=”false”]
window.__defineSetter__(‘el’, function() {
debugger;
});[/crayon]

I threw that into the top of my test case and refreshed, and instantly the browser paused in the console right on the debugger; line. Looking at the previous line in the stack trace showed me this lovely piece of code in the calendar’s Month view:

[crayon striped=”false” lang=”javascript” nums=”false” toolbar=”false”]
if (el = e.getTarget(this.moreSelector, 3)) {
[/crayon]

Oy. I don’t write code like that any more, but this was a holdover from several years ago that simply never got noticed before. A simple fix that literally took seconds to find with this handy little hack.

Don’t Forget to Remove Your Hack

One gotcha that took a few minutes for me to realize… this hack itself, by defining the setter function, is effectively adding the exact same property to the global scope (you end up with a property called window.el by doing this). So Siesta will still see window.el after the test and register a failure, but if you’ve fixed the actual bug, the debugger; line will no longer be hit. This was momentarily confusing, but once I realized what was happening and simply removed my temporary setter override, everything worked with no errors.

Mats Bryntse

Siesta Tips 'n tricks