w3resource

Using with webpack


You can use Jest in projects that use webpack to manage assets, styles, and compilation. Webpack has some unique challenges over other tools because it integrates directly with your application to allow managing stylesheets, assets like images and fonts, along with an expansive ecosystem of compile-to-JavaScript languages and tools.

A webpack example

We will start with a common sort of webpack config file and then translate it to a Jest setup.

// webpack.config.js

module.exports = {
  module: {
    loaders: [
      {exclude: ['node_modules'], loader: 'babel', test: /\.jsx?$/},
      {loader: 'style-loader!css-loader', test: /\.css$/},
      {loader: 'url-loader', test: /\.gif$/},
      {loader: 'file-loader', test: /\.(ttf|eot|svg)$/},
    ],
  },
  resolve: {
    alias: {
      config$: './configs/app-config.js',
      react: './vendor/react-master',
    },
    extensions: ['', 'js', 'jsx'],
    modules: [
      'node_modules',
      'bower_components',
      'shared',
      '/shared/vendor/modules',
    ],
  },
};

In the case where you have JavaScript files that are transformed by Babel, by installing the babel-jest plugin, you can enable support for Babel. You can handle Non-Babel JavaScript transformations with Jest's transform config option.

Handling Static Assets

Next, let us configure Jest to gracefully handle asset files such as stylesheets and images. Usually, these files are not particularly useful in tests so that they can be safely mocked out. However, if you make use of CSS Modules then it is better to mock a proxy for your className lookups.

// package.json

{
  "jest": {
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js"
    }
  }
}```
And also for the mock files themselves:

// __mocks__/styleMock.js

```module.exports = {};
// __mocks__/fileMock.js

module.exports = 'test-file-stub';

Mocking CSS Modules

An ES6 Proxy can be used to mock CSS Modules:

yarn add --dev identity-obj-proxy

Thus all your className lookups on the styles object is returned as-is (e.g., styles.foobar === 'foobar'). This is pretty handy when you are performing React Snapshot Testing.

// package.json (for CSS Modules)

{
  "jest": {
    "moduleNameMapper": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
      "\\.(css|less)$": "identity-obj-proxy"
    }
  }
}

Note that Proxy is enabled in Node 6 by default. If you are not using Node 6 yet, ensure that you invoke Jest using node --harmony_proxies node_modules/.bin/jest.

In the case where moduleNameMapper cannot fulfill your requirements, use Jest's transform config option to specify how assets will be transformed. For instance, a transformer that will return the basename of a file (such that require('logo.jpg'); returns 'logo') can be written as:

// fileTransformer.js

const path = require('path');

module.exports = {
  process(src, filename, config, options) {
    return 'module.exports = ' + JSON.stringify(path.basename(filename)) + ';';
  },
};

// package.json (for custom transformers and CSS Modules)

{
  "jest": {
    "moduleNameMapper": {
      "\\.(css|less)$": "identity-obj-proxy"
    },
    "transform": {
      "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/fileTransformer.js"
    }
  }
}

You have told Jest to ignore files that match a stylesheet or image extension, and require your mock files instead. You can adjust the regular expression such that it matches the file types your webpack config handles.

Note: in cases where you are using babel-jest with additional code preprocessors, you need to explicitly define babel-jest as a transformer for your JavaScript code to map .js files to the babel-jest module.

"transform": {
  "^.+\\.js$": "babel-jest",
  "^.+\\.css$": "custom-transformer",
  ...
}

Configuring Jest to find our files

Since Jest knows how to process our files now, we have to tell it how to find them. For webpack's extensions and modulesDirectories options, direct analogs in Jest's moduleDirectories and moduleFileExtensions options exists.

// package.json

{
  "jest": {
    "moduleFileExtensions": ["js", "jsx"],
    "moduleDirectories": ["node_modules", "bower_components", "shared"],

    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
      "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
    }
  }
}

Note: <rootDir> is a special token that will get replaced by Jest with the root of your project. Most of the time this is the folder where your package.json is located unless you specify a custom rootDir option in your configuration.

Similarly webpack's resolve.root option functions such as setting the NODE_PATH env variable, you can set the NODE_PATH env variable, or utilize of the modulePaths option.

// package.json

{
  "jest": {
    "modulePaths": ["/shared/vendor/modules"],
    "moduleFileExtensions": ["js", "jsx"],
    "moduleDirectories": ["node_modules", "bower_components", "shared"],
    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
      "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js"
    }
  }
}

And finally you just have the webpack alias left to handle. For that you can make use of the moduleNameMapper option again.

// package.json

{
  "jest": {
    "modulePaths": ["/shared/vendor/modules"],
    "moduleFileExtensions": ["js", "jsx"],
    "moduleDirectories": ["node_modules", "bower_components", "shared"],

    "moduleNameMapper": {
      "\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
      "\\.(gif|ttf|eot|svg)$": "<rootDir>/__mocks__/fileMock.js",

      "^react(.*)$": "<rootDir>/vendor/react-master$1",
      "^config$": "<rootDir>/configs/app-config.js"
    }
  }
}

That's all! webpack is a flexible and complex tool, hence you may need to make some adjustments to handle your specific application's needs. However, for most projects, Jest is more than flexible enough to handle your webpack config.

Note: if you have more complex webpack configurations, you can also want to investigate projects like: babel-plugin-webpack-loaders.

Using with webpack 2

webpack 2 provides native support for ES modules. However, we know that Jest runs in Node, and thus will require that ES modules be transpiled to CommonJS modules. As such, if you make use of webpack 2, you will most likely want to configure Babel to transpile ES modules to CommonJS modules only in the test environment.

// .babelrc

{
  "presets": [["env", {"modules": false}]],

  "env": {
    "test": {
      "plugins": ["transform-es2015-modules-commonjs"]
    }
  }
}

Note: Jest will cache files to speed up test execution. If you have updated .babelrc and Jest is still doesn't work, you can try running Jest with --no-cache.

In the case where you are using dynamic imports (import('some-file.js').then(module => ...)), you have to enable the dynamic-import-node plugin.

// .babelrc

{
  "presets": [["env", {"modules": false}]],

  "plugins": ["syntax-dynamic-import"],

  "env": {
    "test": {
      "plugins": ["dynamic-import-node"]
    }
  }
}

Previous: ES6 Class Mocks
Next: Bypassing module mocks



Follow us on Facebook and Twitter for latest update.