Using webpack aliases in mocha tests

18,427

Solution 1

Okay so I realized that everything I was aliasing was in the src/ directory, so I simply needed to modify my npm run test script.

{   
  "scripts": {
    "test": "NODE_PATH=./src mocha ./src/**/test/spec.js --compilers js:babel-core/register --recursive"
  }
}

Probably won't work for everyone, but that solved my issue.

Solution 2

You can also use a babel plugin I authored: https://github.com/trayio/babel-plugin-webpack-alias It will convert your aliased path to relative paths just by including a babel plugin to your .babelrc.

Solution 3

I also encountered the same problem, but with this plugin I solved it.

https://www.npmjs.com/package/babel-plugin-webpack-aliases

The execution command of your "mocha" is not reading the webpack.config.js, so it can not resolve the alias.
By setting this plugin, consider webpack.config.js when compiling with "babel-core/register". As a result, the alias will also be valid during testing.

npm i -D babel-plugin-webpack-aliases

and add this setting to .babelrc

{
    "plugins": [
        [ "babel-plugin-webpack-aliases", { "config": "./webpack.config.js" } ] 
    ]
}

Solution 4

I think I solved this problem. You should use 2 package: mock-require and proxyquire.

Assuming you have a js file like this:

app.js

import action1 from 'actions/youractions';   

export function foo() { console.log(action1()) }; 

And your test code should write like this:

app.test.js

import proxyquire from 'proxyquire';
import mockrequire from 'mock-require';

before(() => {
  // mock the alias path, point to the actual path
  mockrequire('actions/youractions', 'your/actual/action/path/from/your/test/file');
  // or mock with a function
  mockrequire('actions/youractions', {actionMethod: () => {...}));

let app;
beforeEach(() => {
  app = proxyquire('./app', {});
});

//test code
describe('xxx', () => {
  it('xxxx', () => {
    ...
  });
});

files tree

app.js
  |- test
    |- app.test.js

First mock the alias path by mock-require in before function, and mock your test object by proxyquire in beforeEach function.

Solution 5

Danny's answer is great. But my situation is a little bit different. I used webpack's resolve.alias to use all the files under src folder.

resolve: {
  alias: {
    '-': path.resolve(__dirname, '../src'),
  },
},

and use a special prefix for my own modules like this:

import App from '-/components/App';

To test code like this I have to add a command ln -sf src test/alias/- before mocha test and use the NODE_PATH=./test/alias trick Danny camp up with. So the final script would be like this:

{   
  "scripts": {
    "test": "ln -sf src test/alias/-; NODE_PATH=./test/alias mocha ./src/**/test/spec.js --compilers js:babel-core/register --recursive"
  }
}

PS:

I used - because beautifal charactors like @ or ~ are not safe enough. I found the answer for safe characters here

Share:
18,427
Danny Delott
Author by

Danny Delott

Frontend JavaScript engineer in the San Francisco Bay Area.

Updated on June 07, 2022

Comments

  • Danny Delott
    Danny Delott about 2 years

    I'm developing a web app at work in React/Redux/Webpack and am now starting to integrate testing with Mocha.

    I followed the instructions for writing tests in the Redux documentation, but now I have run into an issue with my webpack aliases.

    For example, take a look at the imports section of this test for one of my action creators:

    import expect       from 'expect'                 // resolves without an issue
    import * as actions from 'actions/app';           // can't resolve this alias
    import * as types   from 'constants/actionTypes'; // can't resolve this alias
    
    describe('Actions', () => {
      describe('app',() => {
        it('should create an action with a successful connection', () => {
    
          const host = '***************',
                port = ****,
                db = '******',
                user = '*********',
                pass = '******';
    
          const action = actions.createConnection(host, port, db, user, pass);
    
          const expectedAction = {
            type: types.CREATE_CONNECTION,
            status: 'success',
            payload: { host, port, database, username }
          };
    
          expect(action).toEqual(expectedAction);
        });
      });
    });
    

    As the comments suggest, mocha isn't able to resolve my import statements when they are referencing aliased dependencies.

    Because I'm still new to webpack, here's my webpack.config.js:

    module.exports = {
      devtool: 'eval-source-map',
      entry: [
        'webpack-hot-middleware/client',
        './src/index'
      ],
      output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js',
        publicPath: '/static/'
      },
      resolve: {
        extensions : ['', '.js', '.jsx'],
        alias: {
          actions: path.resolve(__dirname, 'src', 'actions'),
          constants: path.resolve(__dirname, 'src', 'constants'),
          /* more aliases */
        }
      },
      plugins: [
        new webpack.optimize.OccurenceOrderPlugin(),
        new webpack.HotModuleReplacementPlugin(),
        new webpack.NoErrorsPlugin()
      ],
      module: {
        loaders: [{
          test: /\.js$/,
          loaders: ['babel'],
          exclude: /node_modules/,
          include: __dirname
        }]
      }
    };
    

    Also, I'm using the command npm test to run mocha, here's the script I'm using in my package.json.

     {   
       "scripts": {
         "test": "mocha ./src/**/test/spec.js --compilers js:babel-core/register --recursive"
       }
     }
    

    So here's where I get stuck. I need to include the aliases from webpack into mocha when it runs.

    • Issam Zoli
      Issam Zoli over 8 years
      I'm having the same issue, have you found a way to do this ?
    • Danny Delott
      Danny Delott over 8 years
      Yep, just posted my answer. YMMV
  • zzm
    zzm over 8 years
    It seems no work when I use mock-require, error still say can't find module 'actions/xxx'.
  • Issam Zoli
    Issam Zoli over 8 years
    Thanks I'll try this.
  • azium
    azium over 8 years
    All of my stuff is also in src, but not sure how to implement your fix to my test script: find ./src -name '*.test.js' | xargs mocha --require babel-core/register
  • Louis
    Louis over 8 years
    I clicked on your link and judging by the user information there and your user information here, it looks like you are the author of the plugin, so I edited accordingly, because it is against SO rules to mention software you created in an answer without being explicit that you authored it. (Doing so is considered spam, and carries severe penalties.) If somehow you are not the author, you can rollback my edit but make sure to clarify because otherwise, the next person who clicks the link risks making the same inference I did.
  • Louis
    Louis over 8 years
    Also, this answer as it stands is borderline link-only. Adding information about how to install and use the plugin would fix this issue.
  • mikeycgto
    mikeycgto over 8 years
    This is a most excellent solution, especially compared to mocking and proxying require!!
  • Ryan Ore
    Ryan Ore about 8 years
    I'm not using this in production, but I am developing with it now. It seems to do exactly as expected. I'm a fan of alias, and I think using a babel-plugin to solve this problem is the way to go. I haven't looked at the source or anything, but so far works fine.
  • jmancherje
    jmancherje almost 8 years
    this works great for me for a single test run, but I'm not able to get successfully watch files in an aliased directory, have you had any success watching?
  • Danny Delott
    Danny Delott almost 8 years
    I've started using mocha-webpack instead of this workaround. github.com/zinserjan/mocha-webpack