Using webpack aliases in mocha tests

2019-01-21 21:49发布

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.

8条回答
\"骚年 ilove
2楼-- · 2019-01-21 22:06

I assume you would have --require babel-register in your mocha.opts. You can use babel module resolver plugin https://github.com/tleunen/babel-plugin-module-resolver. This allows you to set alias in .babelrc, similar to your webpack alias:

{
  "plugins": [
    ["module-resolver", {
       "alias": {
         "actions": "./src/actions",
         "constants": "./src/constants"
       }
    }]
  ]
}
查看更多
Anthone
3楼-- · 2019-01-21 22:09

To keep aliases in one place like config/webpack.common.js

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

then install babel-plugin-webpack-alias

npm i babel-plugin-webpack-alias --save-dev

then in .babelrc put :

{
    "presets": ["env"],
    "plugins": [
        ["babel-plugin-webpack-alias", {
            "config": "./config/webpack.common.js" // path to your webpack config
        }]
    ]
}
查看更多
Evening l夕情丶
4楼-- · 2019-01-21 22:14

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.

查看更多
ら.Afraid
5楼-- · 2019-01-21 22:17

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.

查看更多
Animai°情兽
6楼-- · 2019-01-21 22:24

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

查看更多
兄弟一词,经得起流年.
7楼-- · 2019-01-21 22:24

I had the exact same issue. It seems impossible to use webpack aliases in require.js or in node's require.

I ended up using mock-require in the unit tests and just replacing the paths manually, like this:

var mock = require('mock-require');

mock('actions/app', '../../src/actions/app');

mock-require is a good tool, because most likely you'd like to mock most of your dependencies anyway instead of using the actual scripts from src.

查看更多
登录 后发表回答