diessi.caBlog
November 19, 2016

How to Exclude CSS, Images, Anything from Unit Tests

Your app needs all those require, but your unit tests may not.

When developing web applications, we deal with assets that our JavaScript tests don’t have to be aware of.

If using Webpack, which enables different imports in your JavaScript files, you’ve configured loaders that your test runner probably know nothing about. Therefore, that SVG image in your React component and the imported CSS will both be parsed like JavaScript in your tests. A lot of confusing errors will be thrown, of course.

So let’s learn to exclude anything your unit tests, from styles (CSS, Sass), images (PNG, SVG), to other specific imports (like SVG images as React components).

Hook your require() calls

By intercepting require() calls, you can make your testing framework ignore what you want to be ignored the way you want it to be ignored.

It’s also useful in isomorphic setups.

Return an empty module

It all comes to returning an empty module for unwanted imports. It looks like this:

module.exports = "";

Jest

For Jest, configure moduleNameMapper to return the empty module for specific extensions. Example:

"jest": {
  "moduleNameMapper": {
    "\\.(css|jpg|png)$": "<rootDir>/empty-module.js"
  }
}

Other testing frameworks

require-hacker is an option for hooking require() calls.

You can also use built-in compilers (like moduleNameMapper in Jest or Mocha compilers), or even only import ignore-styles into your testing framework (which is preconfigured).

I’ll stick to require-hacker and custom configuration because there’s more flexibility.

Get it from npm:

npm install require-hacker --save-dev

Configure

Create a JavaScript file and set custom handlers for specific extensions using require-hacker’s hook() method.

Assuming you want to ignore CSS and PNG files, always return an empty module for them:

import requireHacker from "require-hacker";
requireHacker.hook("png", () => 'module.exports = ""');
requireHacker.hook("css", () => 'module.exports = ""');

Because you’re smart, you’ll store all extensions in a variable and forEach them, so there’s no need to repeat yourself. Example.

Import into your test runner

Let your favourite testing framework know about the require hacking! Some examples, assuming a ignore-utils.js file:

Mocha

Add to your mocha.opts, or use the --require flag:

mocha --require ./ignore-utils
ava

Add to your package.json:

"ava": {
  "require": [
    "./ignore-utils"
  ]
}

Now some files will be treated like shit in your JavaScript tests – which is AWESOME!

Bonus: React null component

You don’t need to load that boring SVG icon of a house to test that critical feature in a React component, right?

Right. In case you’re using svg-inline-loader, which transform your SVG files into React components, you cannot just return an empty module because your test case would be actually expecting a React component. Things will break. Annoying errors will be shown.

So, instead of returning an empty module for SVG files, return an empty React component. Let’s set a custom handler for that!

Configure

This example uses require-hacker. For Jest, export a React null component and set it in moduleNameMapper.

import requireHacker from "require-hacker";

const reactNullComponent = `
  require('react').createClass({
    render() {
      return null;
    }
  })
`;
requireHacker.hook("svg", () => `module.exports = ${reactNullComponent}`);

Conclusion

I’ve spent a lot of time figuring out how to make everything work, because:

  1. At the beginning, it was quite confusing to get what was clearly going on.
  2. There’s a ton of options out there (Webpack null loaders; ignore-styles, which also provide custom handlers; babel-plugin-transform-require-ignore…).
  3. I didn’t want to handle all the ignored extensions the same say.

Yeah… Sometimes our JavaScript unit tests just know too much.