首页 > 解决方案 > 使用 Webpack 的 Karma:伊斯坦布尔覆盖率为 100%(0/0)

问题描述

我正在尝试使用 Webpack (4.27.1) 和 Karma(3.1.3) + Jasmine(jasmine-core 2.99.1) 为 Angular 1.6.6 应用程序实现代码覆盖功能。所有测试都成功通过。然而,Istanbul(0.4.5) 代码覆盖率结果显示100%(0/0)

测试结果输出

===========覆盖摘要=============

语句:100% (0/0) 分支:100% (0/0) 函数:100% (0/0) 行:100% (0/0)

=========================================

HeadlessChrome 73.0.3683(Windows 7.0.0):执行 127 次,共 128 次(跳过 1 次)成功(15.837 秒 / 14.88 秒)总计:127 次成功

业力.config.js

const webpackConfig = require('./webpack.config.js');
webpackConfig.devtool = false;
module.exports = function (config) {
  config.set({
      plugins: [
          'karma-*'
      ],
      singleRun: true,
      frameworks: ['jasmine'],
      basePath: '../',
      exclude: [],
      browsers: ['ChromeHeadless'],
      preprocessors: {
          'test/unit/index_test.js': ['webpack'],
          'app/index.js': ['coverage']
      },
      'reporters': [
          'coverage', 'spec', 'html', 'junit'
      ],
      webpack: webpackConfig,
      coverageReporter: {
          dir: 'target/test-results/coverage',
          reporters: [
              { type: 'html', subdir: 'html' },
              { type: 'lcovonly', subdir: '.' },
              { type: 'text-summary' }
          ],
          instrumenterOptions: {
              istanbul: { noCompact: true }
          },
          check: {
              global: {
                  statements: 90.0,
                  branches: 80.0,
                  functions: 80.0,
                  lines: 90.0
              }
          }
      },
      reportSlowerThan: 100,
      browserNoActivityTimeout: 60000,
      autoWatch: true,
      files: [
          'node_modules/babel-polyfill/dist/polyfill.js',
          'test/unit/index_test.js',
      ]
  });
};

webpack.config.js

const webpack = require('webpack');
const path = require('path');
module.exports = merge.smart(base, {
    entry: {
        app: './src/app.js'
    },
    output: {
        path: path.join(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    devtool: 'eval',
    devServer: {open: true},
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                loader: 'babel-loader'
            },
         ]
      }
  })
;

.babelrc

{
  "presets": ["@babel/preset-env", "@babel/preset-react"],
  "plugins": [
      "angularjs-annotate",
      "@babel/plugin-transform-modules-commonjs"
  ],
  "env": {
      "test": {
      "plugins": ["istanbul"]
    }
  }
}

index_test.js

 import 'core-js/es6/reflect';
 import 'core-js/client/shim';

 require('app/index');
 require('angular');
 require('angular-mocks/angular-mocks');

 beforeEach(() => {
     angular.mock.module('app');
 });

 const testContext = require.context('.', true, /\.spec.js?$/);
 testContext.keys().forEach(testContext);

 const srcContext = require.context('../../app/', false, /app\.module\.js$/);
 srcContext.keys().forEach(srcContext);

标签: angularjswebpackistanbulkarma-coveragekarma-webpack

解决方案


我们从未设法istanbul为我们的 angularjs 项目配置代码覆盖率。这个版本的伊斯坦布尔也已被弃用

我们切换到istanbul-instrumenter-loaderwebpack loader
下面的配置会为我们生成代码覆盖
找不到我们遵循的原始指南,但我会尽可能地描述我们的配置:

package.json devDependencies(与代码覆盖率相关)

{
  "babel-loader": "^8.0.5",
  "istanbul-instrumenter-loader": "^3.0.1", // webpack loader added in coverage tests
  "jasmine-core": "^2.99.1",
  "karma": "^3.1.3",
  "karma-chrome-launcher": "^2.2.0",
  "karma-cli": "^1.0.1",
  "karma-coverage-istanbul-reporter": "^1.4.2", // coverage reporter used in tests
  "karma-html-reporter": "^0.2.7", // html reporter used in tests
  "karma-jasmine": "^1.1.1",
  "karma-ng-html2js-preprocessor": "^1.0.0",
  "karma-sourcemap-loader": "^0.3.7",
  "karma-spec-reporter": "0.0.32",
  "karma-webpack": "^3.0.5",
  "webpack": "4.28.4",
}

测试包版本接近你的

package.json 测试脚本:

我们的业力配置位于./karma子文件夹中

"scripts": {
  "test": "NODE_ENV=development karma start karma/karma.conf.js",
  "cover": "npm test -- --cover --reportHtml", // pass flags to karma.conf
}

业力/karma.conf.js

const path = require('path');
const makeWebpackTestConfig = require('./karma.webpack.config');

module.exports = (config) => {

    const REPORTS_PATH = path.join(__dirname, '../reports/');
    const cover = config.cover || process.env.COVER;
    const webstorm = process.env.WEBSTORM; // Running coverage from inside the IDE 
    const webpack = makeWebpackTestConfig(cover);

    const reporters = config.reportHtml ? ['html'] : [];

    if (!webstorm) reporters.push('spec');
    if (cover) reporters.push('coverage-istanbul');

    config.set({

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '../',

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],

        // list of files / patterns to load in the browser
        files: ['src/main.tests.js'],

        // list of files to exclude
        exclude: [],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
            'src/**/*.js': ['webpack', 'sourcemap'],
            'src/**/*.html': ['webpack'],
            'src/**/*.less': ['webpack'],
        },

        // test results reporter to use
        // possible values: 'dots', 'progress'
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters,

        specReporter: {
            maxLogLines: 5,             // limit number of lines logged per test
            suppressErrorSummary: false,// do not print error summary
            suppressFailed: false,      // do not print information about failed tests
            suppressPassed: false,      // do not print information about passed tests
            suppressSkipped: true,      // do not print information about skipped tests
            showSpecTiming: true,       // print the time elapsed for each spec
            failFast: false              // test would finish with error when a first fail occurs.
        },

        htmlReporter: {
            outputDir: path.join(REPORTS_PATH, 'unit-tests'), // where to put the reports
            // templatePath: null, // set if you moved jasmine_template.html
            focusOnFailures: true, // reports show failures on start
            namedFiles: true, // name files instead of creating sub-directories
            pageTitle: 'Unit Tests', // page title for reports; browser info by default
            urlFriendlyName: true, // simply replaces spaces with _ for files/dirs
            reportName: 'index', // report summary filename; browser info by default

            // experimental
            preserveDescribeNesting: true, // folded suites stay folded
            foldAll: true, // reports start folded (only with preserveDescribeNesting)
        },

        coverageIstanbulReporter: {
            reports: ['lcov', 'text-summary'],
            dir: webstorm ? undefined : path.join(REPORTS_PATH, 'code-coverage'),
        },

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN ||
        // config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: false,

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        browsers: ['RunnerHeadless'],

        customLaunchers: {
            RunnerHeadless: {
                base: 'ChromeHeadless',
                flags: ['--headless', '--no-sandbox', '--disable-gpu', '--disable-translate', '--disable-extensions'],
            },
        },

        // Continuous Integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: true,

        webpack,

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

        // Concurrency level
        // how many browser should be started simultaneous
        concurrency: Infinity,

        client: {
            // Log browser console only locally
            captureConsole: !!process.env.WEBSTORM,
        }
    });

};

同样,由于 karma config 位于子文件夹中,路径(base、reports 等)的配置方式不同。大多数配置是不言自明的。

  • WEBSTORM当从 IDE 内部运行覆盖时,我们设置 了一个 env 变量。
  • 还要记住,需要启用源映射才能正确映射到原始源行,因为原始源是由 babel 转换的。
  • 我们正在使用browsers您的情况可能不需要的自定义配置

业力/karma.webpack.config.js

const makeWebpackConfig = require('../webpack/base-config');

module.exports = (cover) => {

    const defaultConfig = makeWebpackConfig();

    // Remove entry. Karma will provide the source
    defaultConfig.entry = null;

    // Have source maps generated so covered statements are mapped correctly
    defaultConfig.devtool = 'inline-source-map';

    defaultConfig.mode = 'development';

    defaultConfig.optimization = {
        splitChunks: false,
        runtimeChunk: false,
        minimize: false,
    };

    if (cover) {

        defaultConfig.module.rules.push({
            test: /\.js$/,
            use: {
                loader: 'istanbul-instrumenter-loader',
                options: { esModules: true },
            },
            enforce: 'post',
            exclude: /node_modules|\.spec\.js$/,
        });

    }

    return defaultConfig;

};

makeWebpackConfig创建我们在运行开发或生产构建时使用的基本配置,其中包含样式、html、文件等的加载babel-loader器和其他加载器......

  • 任何需要覆盖的设置都会在karma.webpack.conf.js
  • 我想,条目被删除了,卡拉姆无论如何都会覆盖它。
  • 重要 devtool设置为inline-source-map- 这被证明是一场巨大的斗争,因为似乎外部源映射没有被拾取并且源映射在我们设置为inline配置之前不起作用。源图不仅有助于代码覆盖,而且在测试失败和打印错误信息时也有帮助——它将引用原始代码行。
  • 最后,在进行覆盖时,将加载器配置为排除 node_modules 和任何外部源,并排除测试本身

.babelrc 配置

{
  "presets": [
    ["@babel/preset-env", { "modules": "commonjs" }],
    "@babel/preset-react"
  ],
  "plugins": [
    "angularjs-annotate",
    ["@babel/plugin-proposal-decorators", {
      "legacy": true
    }],
    "@babel/plugin-syntax-dynamic-import",
    "@babel/plugin-syntax-import-meta",
    ["@babel/plugin-proposal-class-properties", {
      "loose": true
    }],
    "@babel/plugin-proposal-json-strings",
    "@babel/plugin-proposal-function-sent",
    "@babel/plugin-proposal-export-namespace-from",
    "@babel/plugin-proposal-numeric-separator",
    "@babel/plugin-proposal-throw-expressions",
    "@babel/plugin-proposal-export-default-from",
    "@babel/plugin-proposal-logical-assignment-operators",
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator",
    "@babel/plugin-proposal-do-expressions",
    "@babel/plugin-proposal-function-bind"
  ]
}

应该可以使用您自己的.babelrc配置。{ "modules": "commonjs" }出于某种原因对我们很重要,但现在不记得了

测试入口点 - src/main.tests.js

import '@babel/polyfill';
import './appConfig';
import './main';

const testsContext = require.context('.', true, /\.spec.js$/);
testsContext.keys().forEach(testsContext);

这与您的配置类似,尽管 angular 被导入mainanglar-mocks为每个测试导入,因为我们有很多单独的模块


推荐阅读