Dave Richan Dave Richan - 2 months ago 21
AngularJS Question

Angular 1.* unable to access controller in karma unit test (webpack)

I'm trying to set up a unit test in karma to test an angular 1.* controller. Each time I run the code I get

[ng:areq] Argument 'MoviesListCtrl' is not a function, got undefined


When I have checked the module for the controller appears to get loaded so I'm not sure why its not loading. My project can be found here..

I'm project is available on bitbucket at

https://bitbucket.org/LAD500/movies-angular-webpack/src

to run the tests..

npm install
npm test


Any help with this would be appreciated.

Files I'm working with are..

test - https://bitbucket.org/LAD500/movies-angular-webpack/src/b007e3bced7f3f6249a1b577dc83aa348490d608/test/public/js/app/services/movieslisting.spec.js


import '../../../../../src/js/app/services/movieslisting/';
import '../../../../../src/js/app/directives/movieslist/movieslistctrl';
import '../../../../../src/js/app/browserapp.js';

import {expect} from 'chai';

let createCtrl, scope, moviesListing;

beforeEach(angular.mock.module('BrowserApp'));

beforeEach(inject(($rootScope, $controller)=>{
moviesListing = angular.injector(['ng', 'BrowserApp']).get("moviesListing");
scope = $rootScope.$new();
createCtrl = ()=>{
return $controller('MoviesListCtrl', {
$scope: scope,
moviesListing: moviesListing
});
};
}));

describe('MoviesListCtrl', ()=>{

it('placeholder test', ()=>{
let ctrl = createCtrl();
expect(1).to.equal(1);
});
});





controller



import app from '../../browserapp';

let controllerName = 'MoviesListCtrl';

app.controller('MoviesListCtrl', ['$scope', 'moviesListing', function($scope, moviesListing) {

$scope.moviesList = {
moviesFilter: 'costLowToHigh',
movies: moviesListing.movieCollection
};

$scope.createMovieHeader = movie =>{
let prefix = `${movie.title} (${movie.category}`;
return movie.subcategory ? `${prefix}/${movie.subcategory})` : `${prefix})`;
};

$scope.updateFilter = ()=>{
moviesListing.sortMovies($scope.moviesList.moviesFilter);
};

}]);

export default controllerName;





service thats a dependency



import app from '../../browserapp';

let factoryName = 'moviesListing';

app.factory(factoryName, [function() {
let movieCollection = [];

let currentSortKey = 'costLowToHigh';

let costLowToHigh = (a, b)=>{
if (a.castListTotalCost > b.castListTotalCost) {
return 1;
}
if (a.castListTotalCost < b.castListTotalCost) {
return -1;
}
return 0;
};
let costHighToLow = (a, b)=>{
if (a.castListTotalCost < b.castListTotalCost) {
return 1;
}
if (a.castListTotalCost > b.castListTotalCost) {
return -1;
}
return 0;
};

let sortFunctionsLookup = {
costLowToHigh,
costHighToLow
};

let castListTotalCost = movie => movie.actors.reduce((total, actor) => total + actor.salary, 0);

let addMovie = (movie)=>{
movie.castListTotalCost = castListTotalCost(movie);
movieCollection.push(movie);
movieCollection.sort(sortFunctionsLookup[currentSortKey]);
};

let sortMovies = (sortKey)=>{
currentSortKey = sortKey;
movieCollection.sort(sortFunctionsLookup[currentSortKey]);
};

let reset = ()=>{
movieCollection.length = 0;
};

return {
movieCollection,
addMovie,
sortMovies,
reset
};
}]);

export default factoryName;





app module



import * as angular from 'angular';

var app = angular.module('BrowserApp', []);

app.init = ()=>{
angular.element(document).ready(function() {
angular.bootstrap(document, ['BrowserApp']);
});
};

export default app;





Karma config



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

basePath: '',

frameworks: ['mocha', 'es6-shim'],

files: [
'./node_modules/angular/angular.js',
'./node_modules/angular-mocks/angular-mocks.js',
'test/**/*spec.js'
],

preprocessors: {
'test/**/*spec.js': ['webpack', 'sourcemap']
},

reporters: ['progress'],

port: 9876,

colors: true,

logLevel: config.LOG_INFO,

autoWatch: false,

browsers: ['PhantomJS'],

singleRun: true,

concurrency: Infinity,

webpack: {
devtool: 'inline-source-map',
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loader:"babel-loader"},
{ test: /\.html$/, loader: "html" }
]
},
htmlLoader: {
attrs: false
}
},

webpackMiddleware: {
noInfo: true
}

})
}




Answer

Managed to work this out myself.

the trick is to use a javascript file to generate a bundle for you tests

the javascript file, specs.karma.js looked something like this..

import 'angular';
import 'angular-mocks/angular-mocks';

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

The Karma config..

module.exports = function karmaConfig (config) {
  config.set({
    logLevel: config.LOG_INFO,

    client: { captureConsole: true },

    frameworks: ['mocha', 'es6-shim'],

    reporters: ['progress'],

    files: ['./src/js/specs.karma.js'],

    preprocessors: {
      './src/js/specs.karma.js': ['webpack', 'sourcemap']
    },

    browsers: [ 'PhantomJS'],

    singleRun: true,

    webpack: require('./webpack.test.config'),

    webpackMiddleware: {
      noInfo: 'errors-only'
    }
  });
};

And the webpack.test.config.js..

module.exports = {
  devtool: 'inline-source-map',
  module: {
    loaders: [
      {test: /\.js$/, exclude: /node_modules/, loader:"babel"},
      { test: /\.html$/, loader: "html" }
    ]
  },
  htmlLoader: {
    attrs: false
  }
}