Testing Angular with Gulp-mocha: "Window is not Defined"

Problem

I am setting up a project with Gulp to run unit tests with Mocha, including Angular tests. I have the basic set up working (indexOf, etc.), however when I include angular-mocks I get this error or a node-module error:

ReferenceError in 'gulp-mocha': "Window is not defined"

I've tried including angular-module-mocks, using gulp-mocha-phantomjs... but the result is the same. (With mocha-phantomjs my error was 'Init timeout'.) I've seen many examples of configurations with Mocha and Angular or Gulp and Karma but have not yet found a solution for Gulp, Mocha and Angular alone.

I'm thinking of something similar to this Karma solution to correctly load angular-mocks by specifying it in a config file and forcing Gulp to load it (Angular testing with Karma: "module is not defined"). However, even if this would work, it seems like gulp-mocha does not support loading a configuration file (mocha.opts - https://github.com/sindresorhus/gulp-mocha/issues/26). I would be happy to hear a more straightforward solution.

I am using angular-mocks 1.2.22 and gulp-mocha 1.1.0.

Code snippets:

var mocha = require('gulp-mocha');

gulp.task('test', function () {
    return gulp.src('test/*.js', {read: false})
        .pipe(mocha({reporter: 'nyan', timeout: 400}));
});

test/test.js

var assert = require('assert');

var angular_mocks = require('angular-mocks'); //Fails only when this line is present

//tests
Problem courtesy of: arilaan

Solution

What finally worked for me with Gulp/Browserify/Mocha was using Karma and Mocha combined.

Specifically, I used gulp-karma, and defined the configuration at karma.config.js and used a dummy file for gulp.src as others have done:

gulp.task('test', function () {
  return gulp.src('./foobar.js').pipe(karma({      
    configFile:'karma.config.js',
    action: 'run'
  }))
  .on('error', handleErrors);

});

Then I used this karma.config.js file. I needed the npm modules karma-mocha, karma-chai, and karma-bro. (With only the first two, I was getting 'require is not defined'. Then of course I tried including karma-requirejs, but that does not work with Browserify. Then I tried karma-commonjs, which still didn't work. Then I tried karma-browserify, and got a strange error involving bundle() that no one seems to have solved (https://github.com/xdissent/karma-browserify/issues/46). Karma-bro did the trick.)

I also needed to preprocess each file referenced in the tests as well as the tests themselves. (For using phantomJS also include karma-phantomjs-launcher. And I am using the bower version of angular-mocks simply because it is more recent: v1.2.25 compared to 1.2.22 for npm - but the npm version might work.)

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

  basePath: '',
  // frameworks to use
  frameworks: ['browserify', 'mocha', 'chai'],

  // list of files / patterns to load in the browser
  files: [
    'node_modules/angular/lib/angular.min.js',
    'bower_components/angular-mocks/angular-mocks.js',
    'source/javascript/controllers/*.js',
    'source/javascript/*.js',
    'test/*.js'
  ],

  reporters: ['progress'],

  port: 9876,

  colors: true,

  autoWatch: true,

  browsers: ['PhantomJS'],

  preprocessors: {
    'source/javascript/controllers/*.js': ['browserify'],
    'source/javascript/*.js': ['browserify'],
    'test/*.js': ['browserify']
  }

  });
};

And finally this test passes. At the end I needed to make sure the names of my modules and controllers were consistent (capitals etc.) to resolve 'Module not defined' errors. For debugging I replaced node_modules/angular/lib/angular.min.js with node_modules/angular/lib/angular.js in the files.

describe('Angular', function() {

  describe('App Controllers', function() {

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

    describe('MessageCtrl', function() {

      it('should retrieve the correct amount of messsages', angular.mock.inject(function($controller) {
        var scope = {},
        ctrl = $controller('MessageCtrl', {$scope:scope});
        assert.equal(scope.messages.length, 2);
      }));
    });
  });
});

I do get this: 'WARNING: Tried to load angular more than once.' I can live with it.

Solution courtesy of: arilaan

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.