diff --git a/Gruntfile.js b/Gruntfile.js
index 23032360e849..3cfb82a8af30 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -4,6 +4,7 @@ var serveFavicon = require('serve-favicon');
 var serveStatic = require('serve-static');
 var serveIndex = require('serve-index');
 var files = require('./angularFiles').files;
+var mergeFilesFor = require('./angularFiles').mergeFilesFor;
 var util = require('./lib/grunt/utils.js');
 var versionInfo = require('./lib/versions/version-info');
 var path = require('path');
@@ -141,16 +142,9 @@ module.exports = function(grunt) {
       'jquery-2.2': 'karma-jquery-2.2.conf.js',
       'jquery-2.1': 'karma-jquery-2.1.conf.js',
       docs: 'karma-docs.conf.js',
-      'modules-ngAnimate': 'ngAnimate',
-      'modules-ngAria': 'ngAria',
-      'modules-ngCookies': 'ngCookies',
-      'modules-ngMessageFormat': 'ngMessageFormat',
-      'modules-ngMessages': 'ngMessages',
-      'modules-ngMock': 'ngMock',
-      'modules-ngResource': 'ngResource',
-      'modules-ngRoute': 'ngRoute',
-      'modules-ngSanitize': 'ngSanitize',
-      'modules-ngTouch': 'ngTouch'
+      modules: 'karma-modules.conf.js',
+      'modules-ngAnimate': 'karma-modules-ngAnimate.conf.js',
+      'modules-ngMock': 'karma-modules-ngMock.conf.js'
     },
 
 
@@ -220,6 +214,12 @@ module.exports = function(grunt) {
         dest: 'build/angular-touch.js',
         src: util.wrap(files['angularModules']['ngTouch'], 'module')
       },
+      touchModuleTestBundle: {
+        dest: 'build/test-bundles/angular-touch.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngTouch'),
+        suffix: 'src/module.suffix'
+      },
       mocks: {
         dest: 'build/angular-mocks.js',
         src: util.wrap(files['angularModules']['ngMock'], 'module'),
@@ -229,18 +229,42 @@ module.exports = function(grunt) {
         dest: 'build/angular-sanitize.js',
         src: util.wrap(files['angularModules']['ngSanitize'], 'module')
       },
+      sanitizeModuleTestBundle: {
+        dest: 'build/test-bundles/angular-sanitize.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngSanitize'),
+        suffix: 'src/module.suffix'
+      },
       resource: {
         dest: 'build/angular-resource.js',
         src: util.wrap(files['angularModules']['ngResource'], 'module')
       },
+      resourceModuleTestBundle: {
+        dest: 'build/test-bundles/angular-resource.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngResource'),
+        suffix: 'src/module.suffix'
+      },
       messageformat: {
         dest: 'build/angular-message-format.js',
         src: util.wrap(files['angularModules']['ngMessageFormat'], 'module')
       },
+      messageformatModuleTestBundle: {
+        dest: 'build/test-bundles/angular-message-format.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngMessageFormat'),
+        suffix: 'src/module.suffix'
+      },
       messages: {
         dest: 'build/angular-messages.js',
         src: util.wrap(files['angularModules']['ngMessages'], 'module')
       },
+      messagesModuleTestBundle: {
+        dest: 'build/test-bundles/angular-messages.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngMessages'),
+        suffix: 'src/module.suffix'
+      },
       animate: {
         dest: 'build/angular-animate.js',
         src: util.wrap(files['angularModules']['ngAnimate'], 'module')
@@ -249,14 +273,32 @@ module.exports = function(grunt) {
         dest: 'build/angular-route.js',
         src: util.wrap(files['angularModules']['ngRoute'], 'module')
       },
+      routeModuleTestBundle: {
+        dest: 'build/test-bundles/angular-route.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngRoute'),
+        suffix: 'src/module.suffix'
+      },
       cookies: {
         dest: 'build/angular-cookies.js',
         src: util.wrap(files['angularModules']['ngCookies'], 'module')
       },
+      cookiesModuleTestBundle: {
+        dest: 'build/test-bundles/angular-cookies.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngCookies'),
+        suffix: 'src/module.suffix'
+      },
       aria: {
         dest: 'build/angular-aria.js',
         src: util.wrap(files['angularModules']['ngAria'], 'module')
       },
+      ariaModuleTestBundle: {
+        dest: 'build/test-bundles/angular-aria.js',
+        prefix: 'src/module.prefix',
+        src: mergeFilesFor('karmaModules-ngAria'),
+        suffix: 'src/module.suffix'
+      },
       parseext: {
         dest: 'build/angular-parse-ext.js',
         src: util.wrap(files['angularModules']['ngParseExt'], 'module')
@@ -439,16 +481,9 @@ module.exports = function(grunt) {
   grunt.registerTask('test:jquery-2.1', 'Run the jQuery 2.1 unit tests with Karma', ['tests:jquery-2.1']);
   grunt.registerTask('test:modules', 'Run the Karma module tests with Karma', [
     'build',
+    'tests:modules',
     'tests:modules-ngAnimate',
-    'tests:modules-ngAria',
-    'tests:modules-ngCookies',
-    'tests:modules-ngMessageFormat',
-    'tests:modules-ngMessages',
-    'tests:modules-ngMock',
-    'tests:modules-ngResource',
-    'tests:modules-ngRoute',
-    'tests:modules-ngSanitize',
-    'tests:modules-ngTouch'
+    'tests:modules-ngMock'
   ]);
   grunt.registerTask('test:docs', 'Run the doc-page tests with Karma', ['package', 'tests:docs']);
   grunt.registerTask('test:unit', 'Run unit, jQuery and Karma module tests with Karma', [
diff --git a/angularFiles.js b/angularFiles.js
index 84f041c3065f..205a3ff13f40 100644
--- a/angularFiles.js
+++ b/angularFiles.js
@@ -189,42 +189,34 @@ var angularFiles = {
     'src/angular.bind.js'
   ],
 
-  'karmaModulesBase': [
+  'karmaModules-ngAnimate': [
     'build/angular.js',
     'build/angular-mocks.js',
     'test/modules/no_bootstrap.js',
     'test/helpers/matchers.js',
     'test/helpers/privateMocks.js',
     'test/helpers/support.js',
-    'test/helpers/testabilityPatch.js'
-  ],
-
-  'karmaModules-ngAnimate': [
-    '@karmaModulesBase',
+    'test/helpers/testabilityPatch.js',
     '@angularSrcModuleNgAnimate',
     'test/ngAnimate/**/*.js'
   ],
 
   'karmaModules-ngAria': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgAria',
     'test/ngAria/**/*.js'
   ],
 
   'karmaModules-ngCookies': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgCookies',
     'test/ngCookies/**/*.js'
   ],
 
   'karmaModules-ngMessageFormat': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgMessageFormat',
     'test/ngMessageFormat/**/*.js'
   ],
 
   'karmaModules-ngMessages': [
-    '@karmaModulesBase',
     'build/angular-animate.js',
     '@angularSrcModuleNgMessages',
     'test/ngMessages/**/*.js'
@@ -245,26 +237,22 @@ var angularFiles = {
   ],
 
   'karmaModules-ngResource': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgResource',
     'test/ngResource/**/*.js'
   ],
 
   'karmaModules-ngRoute': [
-    '@karmaModulesBase',
     'build/angular-animate.js',
     '@angularSrcModuleNgRoute',
     'test/ngRoute/**/*.js'
   ],
 
   'karmaModules-ngSanitize': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgSanitize',
     'test/ngSanitize/**/*.js'
   ],
 
   'karmaModules-ngTouch': [
-    '@karmaModulesBase',
     '@angularSrcModuleNgTouch',
     'test/ngTouch/**/*.js'
   ],
diff --git a/karma-modules-ngAnimate.conf.js b/karma-modules-ngAnimate.conf.js
new file mode 100644
index 000000000000..dfc638cf18b5
--- /dev/null
+++ b/karma-modules-ngAnimate.conf.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var angularFiles = require('./angularFiles');
+var sharedConfig = require('./karma-shared.conf');
+
+module.exports = function(config) {
+  sharedConfig(config, {testName: 'AngularJS: isolated module tests (ngAnimate)', logFile: 'karma-ngAnimate-isolated.log'});
+
+  config.set({
+    files: angularFiles.mergeFilesFor('karmaModules-ngAnimate')
+  });
+};
diff --git a/karma-modules-ngMock.conf.js b/karma-modules-ngMock.conf.js
new file mode 100644
index 000000000000..9515a971e6d8
--- /dev/null
+++ b/karma-modules-ngMock.conf.js
@@ -0,0 +1,12 @@
+'use strict';
+
+var angularFiles = require('./angularFiles');
+var sharedConfig = require('./karma-shared.conf');
+
+module.exports = function(config) {
+  sharedConfig(config, {testName: 'AngularJS: isolated module tests (ngMock)', logFile: 'karma-ngMock-isolated.log'});
+
+  config.set({
+    files: angularFiles.mergeFilesFor('karmaModules-ngMock')
+  });
+};
diff --git a/karma-modules.conf.js b/karma-modules.conf.js
index a4efc33b4b77..fbf143053ab1 100644
--- a/karma-modules.conf.js
+++ b/karma-modules.conf.js
@@ -1,14 +1,20 @@
 'use strict';
 
-var angularFiles = require('./angularFiles');
 var sharedConfig = require('./karma-shared.conf');
 
 module.exports = function(config) {
-  var angularModule = process.env.KARMA_MODULE;
-
-  sharedConfig(config, {testName: 'AngularJS: module ' + angularModule, logFile: 'karma-modules-' + angularModule + '.log'});
+  sharedConfig(config, {testName: 'AngularJS: isolated module tests', logFile: 'karma-modules-isolated.log'});
 
   config.set({
-    files: angularFiles.mergeFilesFor('karmaModules-' + angularModule)
+    files: [
+      'build/angular.js',
+      'build/angular-mocks.js',
+      'test/modules/no_bootstrap.js',
+      'test/helpers/matchers.js',
+      'test/helpers/privateMocks.js',
+      'test/helpers/support.js',
+      'test/helpers/testabilityPatch.js',
+      'build/test-bundles/angular-*.js'
+    ]
   });
 };
diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js
index a57cc29ac42b..7e5dc290b533 100644
--- a/lib/grunt/plugins.js
+++ b/lib/grunt/plugins.js
@@ -43,16 +43,7 @@ module.exports = function(grunt) {
 
 
   grunt.registerMultiTask('tests', '**Use `grunt test` instead**', function() {
-    var configFile;
-
-    if (this.nameArgs.includes('modules')) {
-      configFile = 'karma-modules.conf.js';
-      process.env.KARMA_MODULE = this.data;
-    } else {
-      configFile = this.data;
-    }
-
-    util.startKarma(configFile, true, this.async());
+    util.startKarma(this.data, true, this.async());
   });
 
 
diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js
index 79b28bf47f0e..6a2dc6a3c15b 100644
--- a/lib/grunt/utils.js
+++ b/lib/grunt/utils.js
@@ -133,6 +133,16 @@ module.exports = {
 
   build: function(config, fn) {
     var files = grunt.file.expand(config.src);
+    // grunt.file.expand might reorder the list of files
+    // when it is expanding globs, so we use prefix and suffix
+    // fields to ensure that files are at the start of end of
+    // the list (primarily for wrapping in an IIFE).
+    if (config.prefix) {
+      files = grunt.file.expand(config.prefix).concat(files);
+    }
+    if (config.suffix) {
+      files = files.concat(grunt.file.expand(config.suffix));
+    }
     var styles = config.styles;
     var processedStyles;
     //concat
