JavaScript: Mocking an npm install for your tests

I’m writing tests for a node.js tool that I’m working on and hit a snag in trying to mock (i.e., simulate) a dynamic npm install of a module within a subdirectory. I tried a bunch of ideas and ended up with a simple, perhaps not ideal, but effective solution.

TL;DR: Pre-install all of the potentially used npm modules as devDependencies and then symlink the root’s node_modules folder within the subdirectory. In other words: root/subdir/node_modules -> root/node_modules.

A bit of context

The tool (a work in progress), called YA, is supposed to monitor a directory and download the necessary npm modules to help you achieve what you’re trying to do. For example, if you’re using SASS, the tool will try to install a Grunt plugin that helps you watch and compile your SASS files.

The test is supposed to do several things:

  1. Tell YA to monitor a sandbox/ folder within YA’s root folder
  2. Create a SASS file within sandbox/
  3. Tell YA to download what it needs to help process that SASS file
  4. YA should do an npm install of grunt-contrib-sass within sandbox/ (this is the problematic bit)
  5. YA should generate a Gruntfile.js file within sandbox/
  6. YA should then invoke a grunt task that will compile that SASS file into a CSS file within sandbox/

Step 4 is problematic for several reasons:

  • We don’t want to delay the test suite to wait for the download to finish
  • If npm is down, the test will fail
  • I couldn’t find a way to dynamically/programatically npm install a module to a subdirectory

As a result, we should simulate/mock the install of that grunt-contrib-sass plugin.

Everything as a devDependency

I originally didn’t want grunt and all of the necessary/supported grunt plugins to be devDependencies for YA since the tool doesn’t actually need them forĀ development. However, since you can’t dynamically npm install modules to a subdirectory, there didn’t seem to be another way. Also, it’s not a big deal since devDependencies aren’t downloaded for normal users (only developers).

Symlinks within the subdirectory

Now that the node_modules folder in YA’s root has all that we need to invoke grunt tasks during testing, we just need the sandbox/ folder to have access to the grunt plugins. You can use fs.symlink or fs.symlinkSync so that sandbox/node_modules is symbolically linked to YA’s node_modules folder.

Running grunt tasks within a subdirectory

The dynamic Gruntfile will exist within sandbox/, so we need grunt to not look at YA’s root but look at sandbox/Gruntfile.js. You can use the –gruntfile option to specify the directory that contains theĀ gruntfile to use: grunt sass –gruntfile sandbox/.

Hope it helps.