在Angular 2.1.0 Webpack上设置单元测试代码覆盖率

时间:2016-10-21 17:08:00

标签: unit-testing angular

我正在关注this tutorial设置Webpack Angular 2项目。

我可以使用设置运行单元测试,但我尝试使用karma-coverage和remap-istanbul为项目添加代码覆盖,但似乎karma-coverage在coverage-final中没有输出任何内容。 JSON。

我需要在karma配置中添加什么才能使测试配置生效?

这是我当前的配置:

var webpackConfig = require('./webpack.test');

module.exports = function (config) {
    var _config = {
        basePath: '',

        frameworks: ['jasmine'],

        files: [
          {pattern: './config/karma-test-shim.js', watched: false}
        ],

        preprocessors: {
            './config/karma-test-shim.js': ['webpack', 'sourcemap']
        },

        webpack: webpackConfig,

        webpackMiddleware: {
            stats: 'errors-only'
        },

        webpackServer: {
            noInfo: true
        },

        reporters: ['progress'],
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: false,
        browsers: ['PhantomJS'],
        singleRun: true
    };

    config.set(_config);
};

1 个答案:

答案 0 :(得分:4)

您有两种选择,最简单的方法是使用angular-cli。最难的方法是基于该教程进行代码覆盖所需的更改,这是很多。你将被强迫的主要事情之一是改为Webpack 2,我无法使用Webpack 1使remapIstanbulReporter: { reports: { html: 'coverage', lcovonly: './coverage/coverage.lcov' } }, 与karma一起工作。代码覆盖率始终为空。我从angular-cli得到了一些灵​​感,而angular2-webpack-starter来自here,这里有一些变化:

karma.conf.js:添加此内容:

reporters: ['progress'],    

并改变这一点:

reporters: ['progress', 'karma-remap-istanbul'],

到此:

var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var helpers = require('./helpers');

module.exports = {
  entry: {
    'polyfills': './src/polyfills.ts',
    'vendor': './src/vendor.ts',
    'app': './src/main.ts'
  },

  resolve: {
    extensions: ['.ts', '.js']
  },

  module: {
    rules: [
      {
        test: /\.ts$/,
        loaders: ['awesome-typescript-loader', 'angular2-template-loader'],
        exclude: [/\.(spec|e2e)\.ts$/]
      },
      {
        test: /\.html$/,
        loader: 'html'
      },
      {
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        loader: 'file?name=assets/[name].[hash].[ext]'
      },
      {
        test: /\.css$/,
        exclude: helpers.root('src', 'app'),
        loader: ExtractTextPlugin.extract({
          fallbackLoader: 'style-loader',
          loader: 'css-loader'
        })
      },
      {
        test: /\.css$/,
        include: helpers.root('src', 'app'),
        loader: 'raw'
      }
    ]
  },

  plugins: [
    new webpack.optimize.CommonsChunkPlugin({
      // Optimizing ensures loading order in index.html
      name: ['polyfills', 'vendor', 'app'].reverse()
    }),
    new webpack.optimize.CommonsChunkPlugin({
      minChunks: Infinity,
      name: 'inline',
      filename: 'inline.js',
      sourceMapFilename: 'inline.map'
    }),

    new HtmlWebpackPlugin({
      template: 'src/index.html'
    })
  ]
};

webpack配置有很多变化,所以我只是要粘贴整个配置文件,这样更容易:

<强> webpack.common.js

var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');

module.exports = webpackMerge(commonConfig, {
  devtool: 'cheap-module-eval-source-map',

  output: {
    path: helpers.root('dist'),
    filename: '[name].js',
    chunkFilename: '[id].chunk.js',
    sourceMapFilename: '[name].map',
    library: 'ac_[name]',
    libraryTarget: 'var'
  },

  plugins: [
    new webpack.LoaderOptionsPlugin({
      options: {
        tslint: {
          emitErrors: false,
          failOnHint: false,
          resourcePath: 'src'
        },
      }
    }),
    new ExtractTextPlugin('[name].css')
  ],

  devServer: {
    historyApiFallback: true,
    stats: 'minimal',
    watchOptions: {
      aggregateTimeout: 300,
      poll: 1000
    },
    outputPath: helpers.root('dist')
  },
  node: {
    global: true,
    crypto: 'empty',
    process: true,
    module: false,
    clearImmediate: false,
    setImmediate: false
  }
});

<强> webpack.dev.js

var webpack = require('webpack');
var webpackMerge = require('webpack-merge');
var WebpackMd5Hash = require('webpack-md5-hash');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var commonConfig = require('./webpack.common.js');
var helpers = require('./helpers');

const ENV = process.env.NODE_ENV = process.env.ENV = 'production';

module.exports = webpackMerge(commonConfig, {
  devtool: 'source-map',

  output: {
    path: helpers.root('dist'),
    filename: '[name].[chunkhash].js',
    sourceMapFilename: '[name].[chunkhash].bundle.map',
    chunkFilename: '[id].[chunkhash].chunk.js'
  },

  plugins: [
    new WebpackMd5Hash(),
    new webpack.NoErrorsPlugin(),
    new webpack.optimize.UglifyJsPlugin({
        mangle: { screw_ie8: true },
        compress: { screw_ie8: true }
    }),
    new ExtractTextPlugin('[name].[hash].css'),
    new webpack.DefinePlugin({
      'process.env': {
        'ENV': JSON.stringify(ENV)
      }
    }),
    new webpack.LoaderOptionsPlugin({
      options: {
        tslint: {
          emitErrors: true,
          failOnHint: true,
          resourcePath: helpers.root('src')
        },
        htmlLoader: {
          minimize: true,
          removeAttributeQuotes: false,
          caseSensitive: true,
          customAttrSurround: [
            [/#/, /(?:)/],
            [/\*/, /(?:)/],
            [/\[?\(?/, /(?:)/]
          ],
          customAttrAssign: [/\)?\]?=/]
        }
      }
    }),
    new webpack.ContextReplacementPlugin(
      /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
      helpers.root('src')
    )
  ],
  node: {
    fs: 'empty',
    global: true,
    crypto: 'empty',
    process: true,
    module: false,
    clearImmediate: false,
    setImmediate: false
  }
});

<强> webpack.prod.js

var helpers = require('./helpers');
var path = require('path');
var atl = require('awesome-typescript-loader');
var webpack = require('webpack');

module.exports = {
  devtool: 'inline-source-map',
  context: path.resolve(__dirname, './'),
  resolve: {
    extensions: ['.ts', '.js'],
    plugins: [
      new atl.TsConfigPathsPlugin({
        tsconfig: helpers.root('tsconfig.json')
      })
    ]
  },
  entry: {
    test: helpers.root('config/karma-test-shim')
  },
  output: {
    path: './dist.test',
    filename: '[name].bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.ts$/,
        enforce: 'pre',
        loader: 'tslint-loader',
        exclude: [
          helpers.root('node_modules')
        ]
      },
      {
        test: /\.js$/,
        enforce: 'pre',
        loader: 'source-map-loader',
        exclude: [
          helpers.root('node_modules/rxjs'),
          helpers.root('node_modules/@angular')
        ]
      },
      {
        test: /\.ts$/,
        loaders: [
          {
            loader: 'awesome-typescript-loader',
            query: {
              tsconfig: helpers.root('tsconfig.json'),
              module: 'commonjs',
              target: 'es5',
              useForkChecker: true
            }
          },
          {
            loader: 'angular2-template-loader'
          }
        ],
        exclude: [/\.e2e\.ts$/]
      },
      {
        test: /\.html$/,
        loader: 'html'

      },
      {
        test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
        loader: 'null'
      },
      {
        test: /\.css$/,
        exclude: helpers.root('src', 'app'),
        loader: 'null'
      },
      {
        test: /\.css$/,
        include: helpers.root('src', 'app'),
        loader: 'raw'
      },
      {
        test: /\.(js|ts)$/, loader: 'sourcemap-istanbul-instrumenter-loader',
        enforce: 'post',
        exclude: [
          /\.(e2e|spec)\.ts$/,
          /node_modules/
        ],
        query: { 'force-sourcemap': true }
      },
    ]
  },
  plugins: [
    new webpack.SourceMapDevToolPlugin({
      filename: null, // if no value is provided the sourcemap is inlined
      test: /\.(ts|js)($|\?)/i // process .js and .ts files only
    }),
    new webpack.LoaderOptionsPlugin({
      options: {
        tslint: {
          emitErrors: false,
          failOnHint: false,
          resourcePath: `./src`
        }
      }
    }),
    new webpack.ContextReplacementPlugin(
      /angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
      helpers.root('src')
    )
  ],
  node: {
    fs: 'empty',
    global: true,
    process: false,
    crypto: 'empty',
    module: false,
    clearImmediate: false,
    setImmediate: false
  }
}

<强> webpack.test.js

"start": "webpack-dev-server --config config/webpack.dev.js --profile --watch --content-base src/",

<强>的package.json
您需要安装新软件包并将启动脚本更新为:

npm i -D extract-text-webpack-plugin@2.0.0-beta.4 karma-remap-istanbul source-map-loader sourcemap-istanbul-instrumenter-loader tslint tslint-loader webpack@2.1.0-beta.25 webpack-dev-server@2.1.0-beta.3 webpack-md5-hash

并安装这些软件包:

{
  "compilerOptions": {
    "buildOnSave": false,
    "compileOnSave": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "outDir": "dist/out-tsc",
    "noImplicitAny": true,
    "removeComments": false,
    "sourceMap": true,
    "suppressImplicitAnyIndexErrors": true,
    "target": "es5"
  }
}

最后但并非最不重要的是,我们只需要对 tsconfig.json 进行一些更改,因为我们现在正在使用 tslint ,我们添加了一个 tslint.json 档案。

<强> tsconfig.json

{
  "rules": {
    "member-access": false,
    "member-ordering": [
      true,
      "public-before-private",
      "static-before-instance",
      "variables-before-functions"
    ],
    "no-any": false,
    "no-inferrable-types": false,
    "no-internal-module": true,
    "no-var-requires": false,
    "typedef": false,
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      },
      {
        "call-signature": "space",
        "index-signature": "space",
        "parameter": "space",
        "property-declaration": "space",
        "variable-declaration": "space"
      }
    ],

    "ban": false,
    "curly": false,
    "forin": true,
    "label-position": true,
    "label-undefined": true,
    "no-arg": true,
    "no-bitwise": true,
    "no-conditional-assignment": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-variable": true,
    "no-empty": false,
    "no-eval": true,
    "no-null-keyword": false,
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-switch-case-fall-through": true,
    "no-unreachable": true,
    "no-unused-expression": true,
    "no-unused-variable": false,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "radix": true,
    "switch-default": true,
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "use-strict": [
      true,
      "check-module"
    ],

    "eofline": true,
    "indent": [
      true,
      "spaces"
    ],
    "max-line-length": [
      true,
      100
    ],
    "no-require-imports": false,
    "no-trailing-whitespace": true,
    "object-literal-sort-keys": false,
    "trailing-comma": [
      true,
      {
        "multiline": false,
        "singleline": "never"
      }
    ],

    "align": false,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "interface-name": false,
    "jsdoc-format": true,
    "no-consecutive-blank-lines": false,
    "no-constructor-vars": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-finally",
      "check-whitespace"
    ],
    "quotemark": [
      true,
      "single",
      "avoid-escape"
    ],
    "semicolon": [true, "always"],
    "variable-name": [
      true,
      "check-format",
      "allow-leading-underscore",
      "ban-keywords"
    ],
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ]
  }
}

<强> tslint.json:

#!/bin/bash

year=`date +%Y`
month=`date +%m`
day=`date +%d`
prefixe=$month$day$year
logfile="/home/oracle/logs/exec_proc_daily_20_"$prefixe.log

sqlplus / "as sysdba" <<EOF
spool on
spool $logfile
execute Proc_RFC_MAJ_MV_ITIN;
execute Proc_RFC_MAJ_MV_REFGEO;
commit;
quit
EOF

如果您需要,可以检查Angular.io设置(左侧)与我为覆盖工作所做的更改(右侧)之间的差异is available on GitHub