Search code examples
javascriptangularjskarma-runnerbrowserifyisparta

Unit Testing Browserify Project with Karma + Jasmine


I am trying to setup unit testing for a JavaScript plugin that is based on AngularJS. The plugin is bundled with Browserify via Gulp. It depends on external libraries that are injected with wiredep and gulp-inject from the bower_components folder. This is all working beautifully in the generated bundle, but if I try to run a Karma unit test via gulp, I get the following error:

Uncaught TypeError: angular.module is not a function at /tmp/94dbea5947f4758ab1ee6935e2f4b3f1.browserify:365 <- app/js/services/index.js:9:0

In this file, angular is loaded with var angular = require('angular');, and a console.log(angular) gives an empty object.

My karma.conf.js:

'use strict';

const istanbul = require('browserify-istanbul');
const isparta = require('isparta');
const mainBowerFiles = require('main-bower-files');

const karmaBaseConfig = {

    basePath: '../',

    frameworks: ['jasmine', 'browserify'],

    preprocessors: {
        'app/js/**/*.js': ['browserify', 'coverage'],
        '**/*.html': ['ng-html2js']
    },

    browserify: {
        debug: true,
        extensions: ['.js'],
        transform: [
            [["babelify", {"ignore": "/\/bower_components\//"}]],
            'browserify-ngannotate',
            'bulkify',
            'debowerify',
            'brfs',
            istanbul({
                instrumenter: isparta,
                ignore: ['**/bower_components/**', '**/node_modules/**', '**/test/**']
            })
        ]
    },

    ngHtml2JsPreprocessor: {
        stripPrefix: 'app/',
        moduleName: 'templates'
    },

    plugins: [
        'karma-jasmine',
        'karma-coverage',
        'karma-browserify',
        'karma-ng-html2js-preprocessor',
        'karma-chrome-launcher'
    ],

    files: mainBowerFiles({
        filter: '**/*.js',
        paths: {
            bowerDirectory: 'bower_components',
            bowerrc: '.bowerrc',
            bowerJson: 'bower.json'
        }
    }).concat([
        // app-specific code
        'app/js/main.js',

        // 3rd-party resources
        'node_modules/angular-mocks/angular-mocks.js',

        // test files
        'test/unit/**/*.js'
    ]),

    exclude: [],

    reporters: ['progress', 'coverage'],

    port: 9876,

    colors: true,

    autoWatch: false,

    browsers: ['Chrome'],

    singleRun: true
};

const customLaunchers = {
    chrome: {
        base: 'SauceLabs',
        browserName: 'chrome'
    }
};

const ciAdditions = {
    sauceLabs: {
        testName: 'Karma Unit Tests',
        startConnect: false,
        build: process.env.TRAVIS_BUILD_NUMBER,
        tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
    },
    browsers: Object.keys(customLaunchers),
    customLaunchers: customLaunchers,
    reporters: ['progress', 'coverage', 'saucelabs']
};

module.exports = function (config) {
    const isCI = process.env.CI;
    config.set(isCI ? Object.assign(karmaBaseConfig, ciAdditions) : karmaBaseConfig);
};

All main application files are located under app/, bower files in bower_components/, node modules in node_modules/ and test specs in test/unit/.

It is based on this boilerplate: https://github.com/jakemmarsh/angularjs-gulp-browserify-boilerplate.

The error occurs just after Karma has launched Chrome, but before any unit test are executed (I checked with console.log in the unit test).

Any help would be greatly appreciated.


Solution

  • Finally solved it.

    unit.js (gulp unit task):

    'use strict';
    
    import config   from '../config';
    import path     from 'path';
    import gulp     from 'gulp';
    import {Server} from 'karma';
    
    gulp.task('unit', ['copy-bower-components',
        'styles',
        'images',
        'fonts',
        'api',
        'views',
        'browserify',
        'inject'
    ], function (done) {
    
        new Server({
            configFile: path.resolve(__dirname, '../..', config.test.karma),
            singleRun: true
        }, function (exitCode) {
            console.log('Karma has exited with ' + exitCode);
            done();
        }).start();
    
    });
    

    The key here was to run browserify before the unit tests are started. karma.conf.js:

    // Karma configuration
    // Generated on Sat Jan 23 2016 16:43:48 GMT+0100 (CET)
    
    module.exports = function (config) {
        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', 'browserify'],
    
    
            // list of files / patterns to load in the browser
            files: [
                "bower_components/tether/dist/js/tether.js",
                "bower_components/jquery/jquery.js",
                "bower_components/bootstrap/dist/js/bootstrap.js",
                "bower_components/jquery-ui/jquery-ui.js",
                "bower_components/rangy/rangy-core.js",
                "bower_components/rangy/rangy-classapplier.js",
                "bower_components/rangy/rangy-highlighter.js",
                "bower_components/rangy/rangy-selectionsaverestore.js",
                "bower_components/rangy/rangy-serializer.js",
                "bower_components/rangy/rangy-textrange.js",
                "bower_components/angular/angular.js",
                "bower_components/textAngular/dist/textAngular.js",
                "bower_components/textAngular/dist/textAngular-sanitize.js",
                "bower_components/textAngular/dist/textAngularSetup.js",
                "bower_components/KaTeX/dist/katex.min.js",
                "bower_components/angular-bootstrap/ui-bootstrap-tpls.js",
                "bower_components/angular-translate/angular-translate.js",
                "bower_components/angular-translate-loader-static-files/angular-translate-loader-static-files.js",
                "bower_components/angular-cookies/angular-cookies.js",
                "bower_components/angular-translate-storage-cookie/angular-translate-storage-cookie.js",
                "bower_components/angular-translate-storage-local/angular-translate-storage-local.js",
                "bower_components/angular-translate-handler-log/angular-translate-handler-log.js",
                "bower_components/angular-dynamic-locale/src/tmhDynamicLocale.js",
                "bower_components/angular-tour/dist/angular-tour-tpls.min.js",
                "bower_components/ng-sortable/dist/ng-sortable.js",
                "bower_components/moment/moment.js",
                "bower_components/angular-moment/angular-moment.js",
                "bower_components/KaTeX/dist/contrib/auto-render.min.js",
                'dist/js/main.js',
                'node_modules/angular-mocks/angular-mocks.js',
                'test/unit/**/*.spec.js'
            ],
    
            browserify: {
                debug: true,
                transform: [
                    'babelify',
                    'brfs',
                    'bulkify'
                ]
            },
    
            // list of files to exclude
            exclude: ['karma.conf.js'],
    
    
            // preprocess matching files before serving them to the browser
            // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
            preprocessors: {
                'test/unit/**/*.spec.js': ['browserify']
            },
    
    
            // test results reporter to use
            // possible values: 'dots', 'progress'
            // available reporters: https://npmjs.org/browse/keyword/karma-reporter
            reporters: ['progress'],
    
    
            // 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: ['Chrome'],
    
    
            // Continuous Integration mode
            // if true, Karma captures browsers, runs the tests and exits
            singleRun: false,
    
            urlRoot: '/__karma__/'
        })
    };