npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

isolate

v0.14.0

Published

Module isolation framework for CommonJs and AMD module dependencies to support Test Driven Development.

Downloads

30

Readme

Isolate Build Status

Isolate is a tool to allow injection of module dependencies when doing Test Driven Javascript/Coffeescript development. It extends requirejs and node's require to allow isolation of a module under test.

Paired with a Spy/Fake/Mock framework, this allows for a powerful TDD environment where the code under test is properly isolated from its dependencies.

Isolate can be used with either requirejs (AMD) or node's require (CommonJs). It works by hijacking the loader cache in either environment and injecting alternate stand-ins for each of the cached modules.

Why?

When building large or complex Javascript applications, modularity and testing is key. With these two ingredients alone, though, you quickly reach a point where a small mistake in one module can easily lead to large swaths of your tests failing because each module, to some degree, relies on its dependencies to function as expected.

If you instead isolate each module in its test environment with standin implementations of its dependencies, breaking the operation of one module only breaks its related tests. This allows you to develop with more confidence and track down issues faster.

Tools that go well with Isolate

  • mocha as a testing framework
  • chai as an expectations framework

Examples

Since code is worth 1024 words, here is a quick overview of what using Isolate in your code looks like.

requirejs / AMD

path/to/module.under.test.coffee

define ['path/to/dependency'], (dependency)->
  # implementation

path/to/depencency.coffee

define [], ->
  someMethod: -> # some logic

isolate configuration

isolate.map 'path/to/dependency',
  someMethod: -> true

spec file

define ['isolate!path/to/module.under.test'], (moduleUnderTest)->
  dependency = moduleUnderTest.dependencies['path/to/dependency']
  dependency.someMethod() # true

node / CommonJS

path/to/module.under.test.coffee

dependency = require 'path/to/dependency'
# implementation

path/to/depencency.coffee

exports.someMethod = -> # some logic

isolate configuration

isolate.map 'path/to/dependency',
  someMethod: -> true

spec file

moduleUnderTest = module.isolate 'path/to/module.under.test'
dependency = moduleUnderTest.dependencies['path/to/dependency']
dependency.someMethod() # true

Installation

npm install isolate

If you are using requirejs in the browser, Isolate integrates via their Loader Plugins API. You should place isolate.js "in the same directory as your app's main JS file."

Configuration

Isolate maps real module dependencies to fake implementations. It does this via mapping rules that you provide in its configuration. For small projects, you may be able to configure Isolate "just in time", right in your spec file - though it is much more maintainable to instead configure it during the bootstrap phase of running your specs.

For instance, if you are using mocha, you could put your isolate configuration in test/configure-isolate.js and add something like the following to your test/mocha.opts file:

--require test/configure-isolate.js

The test/configure-isolate.coffee file will be similar to:

Isolate = require 'Isolate'
global.isolate = Isolate.isolate

Isolate
  .mapType #...
  .map #...
  .passthru #...
  .mapAsFactory #...

Configuration Options

useRequire
Isolate.useRequire require

useRequire allows you to specify the instance of require.js require to use when isolating AMD modules. If this is not set, Isolate will try to use the global require instance by default.

passthru
Isolate.passthru 'jquery', 'underscore', /lib\/.*/, '/libraries\/.*/' #...

or

Isolate.passthru [ 'jquery', 'underscore', /lib\/.*/, '/libraries\/.*/' ]

passthru allows you to specify that certain modules should be allowed through without injecting a standin. This is good for external libraries that are assumed to be working and stable, or are too complex to realistically build suitable standins for.

This option expects a list of matchers, and can be called multiple times. A matcher can be one of:

  • A RegExp designed to match against the full module path
  • A string staring and ending with a '/', which will be turned into a RegExp instance by removing the '/' from the start and end and calling new RegExp() on the resulting string
  • Any other string which is injected into a RegExp instance which attempts to match the module name
map
Isolate
  .map('some/module', {})
  .map('/.*_controller$/', (options)-> {})
  .map(/.*_view/, (options)-> {})

or

Isolate
  .map
    'some/module'      : {}
    '/.*_controller$/' : (options)-> {}
    '/.*_view/'        : (options)-> {}

map allows you to provide a specific standin implementation to inject for any given matcher (See the passthru section above for details on matchers).

This option expects to be provided a matcher and either a standin instance to inject, or a factory (see map.asFactory below). As syntactic sugar, you can also pass an object map of matcher: standin pairs too (second example above)

Note: Conflicts between passthru, map, and overlapping matchers of each are resolved by choosing the last-defined matching rule.

mapType
Isolate
  .mapType 'function', ->
  .mapType 'object', {}

or

Isolate
  .mapType
    'function': ->
    'object'  : {}

mapType allows you to setup "catch-all" rules to construct standins for modules which failed to match any map or passthru rules defined.

This option expects to be provided a type argument,and either a standin instance to inject, or a factory (see map.asFactory below). As syntactic sugar, you can also pass an object map of type: standin paris too (second example above).

The type argument is compared (case insensitive) to the output of running Object.prototype.toString on the actual module implemenntation. Just the substring containing the type is compared, so for a dependency which is a function, Object.prototype.toString would return [object Function] which means you should specify 'function' as the type to map.

isolateComplete
Isolate.isolateComplete (module)->

isolateComplete allows you to perform last-minute processing of a module before it is injected to the requesting code. The module reference has the dependencies property already prepared.

Mapping to factories instead of literals

Usage as a rule mapper
Isolate
  .mapAsFactory 'some/module',
    (actual, module_path, requesting_module_path)-> {}
  .mapAsFactory '/.*_controller$/',
    (actual, module_path, requesting_module_path)->
      (options)-> {}
  .mapAsFactory /.*_view/,
    (actual, module_path, requesting_module_path)->
      (options)-> {}

or

Isolate
  .map
    'some/module'      : (actual, module_path, requesting_module_path)-> {}
    '/.*_controller$/' : (actual, module_path, requesting_module_path)->
                           (options)-> {}
    '/.*_view/'        : (actual, module_path, requesting_module_path)->
                           (options)-> {}

mapAsFactory allows you to provide a dynamically generated standin implementation to inject with the possibility to customize the standin to the requested module details. mapAsFactory follows the same matcher rules described in the passthru section above.

This option expects to be provided a matcher and a function which generates the standin to inject. The function is provided 3 parameters

  • actual The real module instance which is being "faked" out.
  • requested_module_path The full module path to the module being "faked" out.
  • requesting_module_path The full module path to the module being isolated.

As syntactic sugar, you can also pass an object map of matcher: standin pairs too (second example above)

Note: Conflicts between passthru, map, and overlapping matchers of each are resolved by choosing the last-defined matching rule.

Usage as a modifier to map and mapType
Isolate
  .map /.*_controller/, Isolate.mapAsFactory (actual_module, module_path, requesting_module_path)->
    toString: -> "[Fake for #{module_path}]"
  .mapType 'function', Isolate.mapAsFactory (actual_module, module_path, requesting_module_path)->
    fake_function = ->
    fake_function.toString = -> "[Fake Function for #{module_path}]"
    return fake_function

map.asFactory can also be used to provide a factory function to map and mapType. The factory function will be evaluated when resolving the dependency. The parameters passed to the factory function are the same as described in the Usage as a rule mapper.

Note: mapAsFactory is very helpful when you want to inject a standin for adding some surface area to a module for specs (like wrapping functions in spies), but you still want to check that the integration between the modules hasn't been broken.

Using Isolate in tests

requirejs / AMD

node / CommonJS