Showing posts with label JavaScript. Show all posts
Showing posts with label JavaScript. Show all posts

Tuesday, February 18, 2014

RequireJS Paths: An Off-the-Shelf Plugin Engine

The RequireJS documentation doesn't do justice to the power of the paths configuration option; the sample configuration they include hints at its potential, but it’s never fully spelled out.

Code sample from the RequireJS Configuration Options API documentation

Paths as Mapping

The documentation describes the paths option primarily as a way of resolving the name of a module to an unconventional path from where it must be loaded. Though simple, this use case can be very useful.

One example would be CDNs:

paths: {
'jquery': '//code.jquery.com/jquery-1.10.2.min',
'jqueryui': '//code.jquery.com/ui/1.10.4/jquery-ui',
'webfont': '//ajax.googleapis.com/ajax/libs/webfont/1.5.0/webfont'
}
Paths to jQuery, jQuery UI and Web Font CDNs

Another common use case would be Bower modules:

paths: {
'bootstrap': 'bower_components/bootstrap/dist/js/bootstrap.min',
'jquery-rescope': 'bower_components/jquery-rescope/src/jquery-rescope'
}
Paths to Bootstrap and jQuery-Rescope Bower modules

Paths as Tokens

However what is actually illustrated in the documentation isn’t a one-to-one mapping -- unless they’re loading the v1.0.js module from the some folder -- it’s much more powerful!

By mapping the path “some” to “some/v1.0,” they are creating a replacement token which will interpret requiring “some/module” as needing to actually load “some/v1.0/module.”

The specific example in the documentation demonstrates how RequireJS frees you from explicitly referencing versions in your modules while allowing you to maintain that information on the file system. Which is interesting, but it doesn't exactly solve a critical problem.

What this example demonstrates generally, is an ability to swap compatible modules -- not just across versions, but with completely independent implementations -- via configuration without needing to write any special support for it in our applications. Add to this the fact that RequireJS is configured in JavaScript (as opposed to through a static file of some kind), and we can generate a path configuration based on whatever criteria we can imagine.

Some possible applications of this include:
  • browser feature detection
    • HTML5: map a path for “storage” to a folder of modules using localStorage
    • legacy: map the “storage” path to folder of modules using a database-backed web service
  • layering security
    • anonymous users: map to modules which only display messages that authentication is required
    • authenticated users: map to modules which actually attempt privileged operations
  • sharing a common code base
    • mobile: map to modules which implement functionality using Cordova APIs
    • Web: map to modules which implement functionality using browser and server APIs

Example

As a concrete example, I’ve written a simple demonstration which has two different modes of interacting with the user, and it switches between them without alteration to the code (or even any awareness of there being different modes in the app code) outside of the RequireJS configuration.

Setup

The entry point is my main.js file. This is where I bootstrap the environment before launching my application.

/*globals requirejs*/
(function (require) {
 'use strict';
 require(['paths'], function (paths) {
         require.config({
             paths: paths
         });

         ...
 });
})(require);
Configuring require using paths module

I’ve written the main module to load the paths as a separate module, so that it can be solely concerned with start-up operations.

The paths module interpolates the app's configuration into a RequireJS-compatible paths object.

define([
    'config'
], 
function (config) {
    'use strict';
    
    return {
                'jquery': '//code.jquery.com/jquery-1.10.2.min',
                'output': 'output/' + config.outputMode
            };
});
Mapping output path token based on configuration

The config module is "where the magic happens." For this demo, I'm determining the environment state based on the query string and sets it as the app's output mode.

define(function () {
    'use strict';
    
 function getOutputMode () {
        var outputModes = 
        [
         'obnoxious',
         'polite'
        ],
        currentOutputMode = 0,
        requestedOutputMode;
  
        // Dynamically set mode from query string
        requestedOutputMode = parseInt(
            (window.location.search.match(/[?&]mode=(\d+)/) || [])
            [1]);

        if(requestedOutputMode > 0 && requestedOutputMode < outputModes.length) {
            currentOutputMode = requestedOutputMode;
        }

        return outputModes[currentOutputMode];
 }
    
    var exports = {};
 
    exports.outputMode = getOutputMode();
    
    return exports;
});
Setting output mode in configuration based on query string

Now that RequireJS is configured to load modules from the correct path, we can return to the main module and start our demo app.

/*globals requirejs*/
(function (require) {
 'use strict';
 require(['paths'], function (paths) {
         ...

  // Launch our app
  require(['app'], function (DemoApp) {
   var demo = new DemoApp();

   demo.run();
  });
 });
})(require);
Launching demo app


The Demo App

Within the demo app, I require in the three output modules and assign the returned classes to local names.

define(
[
 'output/prompt',
 'output/confirm',
 'output/message'
],
function (Prompt, Confirm, Message) {
 ...
});
App module requiring and accepting prompt, confirm and message output modules

Then, within the body of the demo app, I can use the required-in modules, according to their individual contracts, in complete ignorance of how they are implemented.

 function EchoDemoApp () {
  var me = this;
 }
 
 EchoDemoApp.prototype.prompt = function () {
  var me = this,
   promptMessage = new Prompt('What do you want to say?');
   
  promptMessage.display(function (response) {
   var input = response || 'nothing',
    confirmation = new Confirm('Are you sure you want to say "' + input + '"?');
   
   confirmation.display(function (confirmed) {
    var output;
    
    if(confirmed) {
     output = new Message(input);
     output.display();
    } else {
     me.prompt();
    }
   });
  });
 };

 EchoDemoApp.prototype.run = function () {
  var me = this;
  
  me.prompt();
 }
Paths to jQuery, jQuery UI and Web Font CDNs
View a running version of the demo
Browse, download or fork the source from this article on GitHub

Monday, February 3, 2014

TDD *is* BDD and Multi-Class Modules

My friend and colleague, Dan Martinez, recently pointed me to Ian Cooper’s NDC talk, TDD where did it all go wrong. This is definitely recommended viewing for practical and “back to basics” TDD for neophytes and people who think they’re experts alike.

For me, I took away some new concepts, but it also clearly validated a lot of the practices that have emerged in my work and that I’ve picked up from my colleagues.

A particular point which is worth repeating is that TDD is BDD. Good tests test behavior, and behavior is expressed through public interfaces.

Restraining tests to interfaces doesn’t lead to an underpowered test suite, because all internal code should exist in the service of some behavior. Therefore testing the public interface for that behavior will provide complete coverage; any uncovered code implies unintended behavior.

This point is easily forgotten and probably often dismissed by recent converts as lazy or impure. However it is powerful, because it leads to focused development, lean suites and less rigid tests.

Applied to JavaScript Modules

Adding to this idea, it has implications for my current world of JavaScript in an unexpected way.

Modules, whether following the simple IIFE pattern or a CommonJS standard, export all functionality in an inherently public fashion. That implies, for the purposes of TDD, that a module is a behavior-level component. Which means that non-behavioral components, including support classes, must be completely scoped within and hidden by a module.

This is a somewhat hard pill to swallow for me, because, as a long-standing style practice (that I had ingrained in me from StyleCop in C#), I follow a “one class/one file” rule. But I’m trying to look at it as permission to break the rules and get that edge back into my coding!

Yeah. I’m bad.

Tuesday, October 15, 2013

jquery-rescope: DOM Mocking Made Easy

It’s easy to declare a technique to be correct, but it’s another thing to try and make it practical.

So after writing about Oreo Testing, I decided to convert the getJqueryMockDocument function from my sample into something that could be distributed and used by others.  Given its nature, a  jQuery plugin seemed like the most natural option.

It should surprise no one who's read this far that I rewrote it using TDD to ensure that it:

  • follows AMD conventions
  • follows jQuery’s basic and advanced guidelines for plugin creation
  • performs its expected functions across all common, current browsers (of course)

Usage: Updating the Façade Example

Its usage should be straight-forward enough: invoke the plugin to separate a selected (or created) node from the current DOM and isolate it into a new one.  

Using our earlier façade example as a demonstration, it would be changed by:
  1. Adding a reference to the rescope plugin:
    <script src="sampleViewFacade.js" type="text/javascript"></script>
    <script src="sampleViewFacade.tests.js" type="text/javascript"></script>
    <script src="jquery.min.js" type="text/javascript"></script>
    <script src="jquery-rescope.js" type="text/javascript"></script>
    
    HTML script tags including test JavaScript as well as jQuery and rescope plugin
  2. Removing the getJqueryMockDocument function and replace all calls to it with $(...).rescope():
    $mock = $('<div class="sample"><input id="correctElement" type="checkbox" /></div>').rescope();
    …
    $mock = $('').rescope();
    …
    $mock = $('<button id="clickme">').rescope();
    
    Replacement lines for calls to getJqueryMockDocument using rescope
In my opinion, not only is this obviously much easier to incorporate into other projects, this is much more concise and readable.

Set it Free(ly licensed on GitHub)! 

The last step in distributing software is making it available! I have done so, not through some run-of-the-mill lazy link on my blog!  Oh no!

I tried something new (and long overdue) and created a repository for jquery-rescope on GitHub. I cordially invite you to browse the code, download it, submit pull requests, write up issues and otherwise make me feel like a real open sourcerer!

More to Come? 

For all the pixels I've spilled on this topic, I doubt this will be the last time that I write about it.

For example I stumbled into the interesting practice of wrapping each test modules in an IIFE so that I could run them all from a single HTML page without corrupting each test's namespace.

Most importantly, I haven’t found or heard any feedback that this combination of black-box and white-box testing is widely known or practiced.  Given all of its advantages, I can imagine myself continuing to advocate it as the Right Way to do TDD from now on.

jquery-rescope plugin on GitHub

Download my updated code from this article
Browse the source from this article on GitHub

Friday, September 6, 2013

jQuery in TDD with a View Façade (and Introducing Oreo Testing)

In my post jQuery in TDD is Serious Business (Don't Mock It), I argued that jQuery (and any DOM manipulation) should be isolated in a view façade to avoid mocking its behavior and creating brittle tests. This is fine advice for creating a resilient view layer, but it doesn't address how to avoid the same problem when the view façade needs to be tested.

White-Box Testing

While writing our view, we had an implicit goal to isolate it from everything external to it, including other units (i.e. using mocks) and third parties (e.g. encapsulating jQuery into a view façade). Besides ascending to a higher plane of code modularity, this isolation serves two critical purposes:
  1. allowing a tester to quickly identify the exact unit and test that is failing when a failure occurs without having to walk down the application stack.  In other words, a failure in testing equals a failure in exactly one unit
  2. freeing us to start and finish any unit of work without having to wait for a depended-upon unit to be written
In my experience, this leads to Test-Driven Development (TDD) practically assuming a white-box testing approach to development.

If we try and apply that wisdom that to our view façade however, we encounter all of the same problems we had been trying to avoid.  At the very edge of our stack, with nowhere left to push jQuery, we have to face the music.  We have to either mock jQuery or find another way.

Black-Box Testing

One of the major objections I raised to mocking jQuery, was how it turns virtually any code change into a test failure even if the unit's behavior hasn't changed.  If we rephrase that positively as “we want to write tests that pass, regardless of the implementation, so long as they produce the correct results,” we stumble upon black-box testing.

In black-box testing we prepare a test environment with known data, perform the tested action and then verify success against expected output.

In our example, this means creating a free-standing DOM for testing purposes, contextualizing a jQuery instance to it and passing that as the dependency to our view façade.

Example

Without going through the individual steps this time, let's set up a new test environment with QUnit and include jQuery itself on the test page this time.

As a refresher, the interface we defined for our view façade is:




facade = {
 getCheckbox: function () {
 },
 bindClick: function (element, callback) {
 },
 getFieldset: function () {
 },
 toggleElement: function (element) {
 }
};
I apologize for the horrible naming scheme. It can be so hard to make up with a decent example.

Let's dig in and write a quick outline for a test against the getCheckbox method:




test('getCheckbox returns the correct checkbox', function () {
 'use strict';
 var checkbox,
  foundCheckbox;
 
 unit = new SampleViewFacade($);
 foundCheckbox = unit.getCheckbox();
  
 equal(foundCheckbox, checkbox, '');
});
Test that instantiates the unit, calls the getCheckbox method and an assert comparing the returned value and a test value.

If we were writing a white-box test, our next step would be to mock jQuery to return checkbox as a test value for the expected call chain.  Instead we need to construct a test environment with a DOM supplied by the and a jQuery instance that interacts with that environment.

Contextualizing jQuery to a Mock DOM

To use jQuery with predictable data, I've written the following function to inject arbitrary HTML into the body element of an iframe and return a jQuery object confined to its context.

function getJqueryMockDocument (html) {
 var $ref, $doc;

 $('iframe#jqueryMockDocument').remove();
 $ref = $('<iframe id="jqueryMockDocument" style="display: none;">').appendTo('body').contents();
 $doc = $ref.extend(function (selector) { return $ref.find(selector); }, $ref);

 // For IE (head missing immediately on iframe add)
 if ($doc('head').length === 0) {
  $doc[0].write('<head>');
 }

 // For IE (body missing immediately on iframe add)
 if ($doc('body').length === 0) {
  $doc[0].write('<body>');
 }

 if (html) {
  $doc('body').append(html);
 }

 return $doc;
}

This function creates (or replaces) an IFrame on the page to allow access to elements and nodes outside of the body, such as head and document.

It then selects the contents of the body within that IFrame and extends it with a wrapper of jQuery's find method.  This is to complete the mock to allow $ selector calls.

The final part appends the html parameter to the body of the new IFrame.

The remainder is dealing with the fact that IE was confused, got up from the kid's table and wandered over to where adult browsers were talking.

Writing the Mock HTML

Now that we can convert an HTML fragment into a jQuery instance running against it as a DOM, let's put it to use!

Since our application is only as large as my contrived example, it's reasonable to assume that the "correct" checkbox will be the first checkbox within our application's namespace.  Our HTML will therefore be an outer div with a class for our namespace, sample, and a checkbox we expect to have returned.  For the sake of testing, we'll set an attribute on the checkbox to make confirming we've selected the correct checkbox easier.

Our updated variable declaration is now:
var foundCheckbox,
 $mock = getJqueryMockDocument('<div class="sample"><input id="correctElement" type="checkbox" />');

Our updated assert now is:
equal($(foundCheckbox).attr('id'), 'correctElement', 'Found the checkbox with the expected ID.');

Finally, with our contextualized jQuery we need to update our test statement to:
unit = new SampleViewFacade($mock);

Following the TDD process, we'll write code to make the test pass by
  1. defining the SampleViewFacade class 
  2. adding a getCheckbox method
  3. accepting jQuery as a parameter in the constructor
  4. selecting and returning the checkbox element
which looks like:
function SampleViewFacade ($) {
 var me = this;
 
 me.getCheckbox = function () {
  var result = $('.sample :checkbox:first').eq(0);
  
  if (result.length === 1) {
   return result[0];
  }
  
  return null;
 };
}

Black vs. White Box Testing

This example demonstrates that we can write a test that accurately validates success without mocking jQuery. If this is advantageous, and works for our view façade, then what’s the catch?  Why didn't we test the view this way?

Developing our view in TDD without any opinion of its internals would have meant:

  • being unable to use internal-aware mock dependencies
  • needing to wait for the view façade to be written
  • speculating on the view's requirements when defining the view façade interface
  • tests failing both if the view failed or if the view failed because the view façade failed

Since we have a workable white-box testing regime for developing our views, this isn't such a hard a pill to swallow. In terms of our view façade, the first three are similarly easy to accept, because they aren't problems in this case, but how do we handle the issue of isolating the view façade as a unit for testing?

Since the only dependency is jQuery and we have to operate under the assumption that it functions correctly and according to their documentation, there is no ambiguity when a test fails: it fails within our unit.  Therefore black-box testing has none of these shortcomings for the view façade, because the view façade is already isolated.

Oreo Testing

This example has focused on jQuery, but it applies to any third-party integration -- which implies an overall approach for TDD from end-to-end.

At either end of the application stack, where units interact with the host environment, black-box testing can be employed without losing any desirable aspects of white-box testing and should be employed to minimize test brittleness.

Within the interior of an application, where units only interact with other application units, white-box testing should be employed to isolate units, define interfaces and decrease wait time in development.

Black edges and white interiors

Download my complete code from this article
Browse the source on GitHub

Update 9/6/2013

As described in my later post, My "View Adapter" Was Just a Façade, I updated this post to refer to a "view façade" instead of a "view adapter" as it had originally been written.