typescriptwebpackkarma-runnerangular-mock

Angular 1.5 Karma unit test loads ng-mock twice


I have a web app written with Typescript 2.4.2, compiled by latest Webpack version (2.7.0).

I'm in the process of adding Karma tests using Jasmine as the assertion library.

This is my karma config file:

'use strict';

const webpack = require('./webpack.config');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const _ = require('lodash');

_.remove(webpack.plugins, plugin => plugin instanceof CleanWebpackPlugin);

webpack.module.rules.forEach(rule => {
    if (rule.loader === 'ts-loader') {
        rule.exclude = /sg\.d\.ts/;
    }
}); 

module.exports = function (config) {
    config.set({
        webpack,

        mime: {
            'text/x-typescript': ['ts','tsx']
        },

        // 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: [
            { pattern: 'app/scripts/**/*.test.ts', watched: true}
        ],

        preprocessors: {
            "app/scripts/**/*.test.ts": ["webpack"]
        },

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

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

        // 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: true,

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

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

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

On the first test suite I've had no problems, it looks something like this:

import { default as moduleName } from "../module";
import { ListService, IListRequest } from "./listService";
import * as angular from 'angular';
import "angular-mocks";
import "jasmine";

const { module, inject } = angular.mock;

describe("List service", () => {

    let serviceToTest: ListService;

    let rootScope: ng.IRootScopeService;
    let httpBackend: ng.IHttpBackendService;

    beforeEach(module(moduleName));

    beforeEach(() => {
        inject(['$rootScope', '$httpBackend', ListService.SERVICE_NAME,
            ($rootScope: ng.IRootScopeService, $httpBackend: ng.IHttpBackendService, listService: ListService) => {
                serviceToTest = listService;
                rootScope = $rootScope;
                httpBackend = $httpBackend;
            }]);
    });

    it("service should not be null", () => {
        expect(serviceToTest).toBeDefined();
    });

    // Other tests
});

The problem started when I tried writing a test suite for another service:

import { default as moduleName } from "../module";
import { RedirectionService } from "./RedirectionService";
import * as angular from 'angular';
import "angular-mocks";
import "jasmine";

const { module, inject } = angular.mock;

describe('Redirection service', () => {
});

As you can see the second test suite is empty.

My problem is that as soon as I add that second suite, Angular starts throwing errors - it tries loading Angular and ng-mock twice, once for each new file that imports them.

The error that is thrown is:

Error: [$injector:modulerr] Failed to instantiate module ngLocale due to: RangeError: Maximum call stack size exceeded

A quick Google search produces many thread on the matter - saying this error pops up when Karma loads ng-mock twice. Also, debugging the code on Chrome confirms that it is ngMock that fails to initialize, and Angular produces a warning that Angular was loaded twice.

What can I do about this? It seems to me as if karma treats each test file separately, so it doesn't use Webpack to bundle Angular into a single module but rather compiles each file on its own and adds Angular and ng-mock again.


Solution

  • I've gone a different route, couldn't figure out how to solve that problem.

    My webpack config now has this:

     entry: { entry: './app/scripts/main.ts'},
        output: {
            filename: isDev ? "[name].js" : '[name]-[hash].js',
            path: path.resolve('dist'),
            pathinfo: isDev
        },
    ...
    if (isDev) {
        module.exports.devtool = 'source-map';
        module.exports.entry.test = './test/test-main.ts';
    }
    

    And I dropped all the webpack stuff in the Karma config.

    I'm telling Webpack to compile another bundle, with a simple entry point TS file that reads:

    Import((<any>require).context('../app/scripts/', true, /.*\.test\.ts$/i), (_: any) => {});
    

    Karma is watching the output bundle file for changes.