marcoslin / angularAMD

Facilitate use of RequireJS in AngularJS
http://marcoslin.github.io/angularAMD
MIT License
734 stars 171 forks source link

Karma testing issue: "Error: angularAMD not initialized. Need to call angularAMD.bootstrap(app) first." #103

Closed mpiasta-ca closed 10 years ago

mpiasta-ca commented 10 years ago

My web app is working fine. But I can't get the karma test working, I've been stuck for hours. Unable to inject a $controller. Any help would be greatly appreciated! I searched Google but none of those solutions are working.

Latest error when running karma test for home-controller_test.js: Error: angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.

...and If I switch angularAMD.inject to just inject it says: ReferenceError: Can't find variable: inject

...and if I add angular-mocks to the define(['angular-mocks'] I get the error: Error: [ng:areq] Argument 'HomeController' is not a function, got undefined

Structure:

- src/
--- components/
------ config/
--------- require.conf.js (aka. *require-main.js*)
------ libs/
--------- angular/
----------- [...]
--------- angular-mocks/
----------- [...]
--------- angular-routes/
----------- [...]
--------- angularAMD/
----------- [...]
--------- requirejs/
----------- [...]
------ home/
--------- home-controller.js
--------- home-controller_test.js
--- app.js
--- index.html
- test/
--- app.js
--- karma.conf.js 
--- require.conf.js (aka. *test-main.js*)

test/karma.conf.js:

module.exports = function (config) {
  config.set({
    basePath: '../',
    frameworks: ['jasmine', 'requirejs'],
    files: [
      {pattern: 'src/**/*.js', included: false},
      {pattern: 'test/app.js', included: false},
      'test/require.conf.js'
    ],
    exclude: [
      'src/components/config/*'
    ],
    preprocessors: { },
    reporters: ['progress'],
    port: 2080,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    browsers: ['PhantomJS'],
    singleRun: true
  });
};

test/require.conf.js:

'use strict';

var allTestFiles = [],
  TEST_REGEXP = /(spec|test)\.js$/i;

Object.keys(window.__karma__.files).forEach(function (file) {
  if (TEST_REGEXP.test(file)) {
    allTestFiles.push(file);
  }
});

require.config({
  baseUrl: "/base",
  paths: {
    'angular': 'src/components/lib/angular/angular',
    'angular-route': 'src/components/lib/angular-route/angular-route',
    'angular-mocks': 'src/components/lib/angular-mocks/angular-mocks',
    'angularAMD': 'src/components/lib/angularAMD/angularAMD',
    'app': 'test/app'
  },
  shim: {
    'angular': { 'exports': 'angular' },
    'angular-route': ['angular'],
    'angular-mocks': {
      deps: ['angular'],
      'exports': 'angular.mock'
    },
    'angularAMD': ['angular']
  },
  deps: allTestFiles,
  callback: window.__karma__.start
});

test/app.js:

define(['angularAMD'], function (angularAMD) {
  var app_name = "my-web-app",
    app = angular.module(app_name, []);

  app.__appname = app_name;

  var elem = document.createElement('div');
  angularAMD.bootstrap(app, false, elem);

  return app;
});

src/home/home-controller.js:

define(['app'], function (app) {
  app.controller('HomeController', function ($scope) {
    $scope.message = 'Test $scope data injection. This text is from the controller.';
  });
});

src/home/home-controller_test.js:

define(['app', 'angularAMD'], function (app, angularAMD) {
  describe('home/home-controller.js', function () {
    var scope, ctrl;

    beforeEach(angularAMD.inject(function ($rootScope, $controller) {
      scope = $rootScope.$new();
      ctrl = $controller('HomeController', {
        $scope: scope
      });
    }));

    it('should define more than 5 awesome things', function () {
      expect(scope.message).toBeDefined();
    });
  });
});

If I change that around to the following, it gives me this error: Error: [ng:areq] Argument 'HomeController' is not a function, got undefined

src/home/home-controller_test.js:

define(['app',  'angularAMD', 'angular-mocks'], function () {

  describe('home/home-controller.js', function () {
    var scope, ctrl;

    beforeEach(inject(function ($rootScope, $controller) {
      scope = $rootScope.$new();
      ctrl = $controller('HomeController', {
        $scope: scope
      });
    }));

    it('should define more than 5 awesome things', function () {
      expect(scope.message).toBeDefined();
    });
  });

});
marcoslin commented 10 years ago

What version of angularAMD are you using? If 0.2.x, you need to return the result of .bootstrap:

test/app.js

  ...
  return angularAMD.bootstrap(app, false, elem);
mpiasta-ca commented 10 years ago

@marcoslin AngularAMD version is 0.2.1. Hmm, I'm trying with return, but still looks like Error: angularAMD not initialized.

test/app.js:

define(['angularAMD'], function (angularAMD) {
  'use strict';

  var app_name = "web-app",
    app = angular.module(app_name, []);

  app.__appname = app_name;

  var elem = document.createElement('div');
  return angularAMD.bootstrap(app, false, elem);
});

Does it have to use the notation angularAMD.inject(...)? When I include angular-mocks as a dependency in the define for the test, looks like inject(...) may be working. But then there error I get is that Error: [ng:areq] Argument 'HomeController' is not a function, got undefined, so I don't know how do I pass in the controller.

src/home/home-controller_test.js:

define(['angularAMD', 'HomeController', 'angular-mocks'], function (angularAMD, HomeController) {
  describe('home/home-controller.js', function () {
    var scope, ctrl;
    beforeEach(inject(function ($rootScope, $controller) {
      scope = $rootScope.$new();
      ctrl = $controller('HomeController', {
        $scope: scope
      });
    }));
    it('should have scope.message string in controller', function () {
      expect(scope.message).toBeDefined();
    });
  });
});

Any other ideas on what I could try?

mpiasta-ca commented 10 years ago

@marcoslin Take a look. I've setup the files on runnable.com. Hit the green Run button, or type gulp in the terminal. That will run the karma tests.

marcoslin commented 10 years ago

I tried your runnable and changed a few things but the error I am getting is: "Mismatched anonymous define() modules", which seems to be a project with version of requirejs that is being used with karma.

Unfortunately, I do not know gulp (yet!!!) so having a bit of difficulty trying to figure out what is wrong.

mpiasta-ca commented 10 years ago

@marcoslin OK sorry, yeah that was first time I used gulp-karma npm package. I set it up incorrectly, set it up using karma npm package instead and now it's working right.

I updated the runnable.com project so now when you hit Run it should return Error: angularAMD not initialized. Need to call angularAMD.bootstrap(app) first.

mpiasta-ca commented 10 years ago

@marcoslin For the karma tests, do I even need to use AngularAMD? I'm wondering if I can just use a karma setup from another yeoman angular + require generator (without AMD) as an example of how to build my tests. I don't need the karma/jasmine tests to lazy load, right lol.

marcoslin commented 10 years ago

@mpiasta-ca I spend the morning trying to get the runnable to work and I was getting very weird errors. Then my day job called...

I will take your code and create a new github project but with grunt instead of gulp. Stay tuned.

mpiasta-ca commented 10 years ago

@marcoslin I found your runnable fork. Interesting -- if you keep typing gulp in terminal over-and-over, it works some times, fails some times. I'm getting the same result running karma tests in WebStorm (without using gulp at all). So it seems like the code itself is correct, but the order in which karma is loading the files is causing an issue.

marcoslin commented 10 years ago

@mpiasta-ca exactly. I couldn't figure out why it only works sometime so I decided to create an independent project. I will post here when done.

marcoslin commented 10 years ago

@mpiasta-ca I created the following bare minimum project for unit testing with angularAMD: https://github.com/marcoslin/angularAMD-karma

Based on your runnable.com code, here is what needed to be fixed:

  1. require.conf.js: home_controller is not defined anywhere and it's dependency to app need to be set.
  2. home-controller_test.js: you are not loading home_controller hence you are getting the 'HomeController' is not a function, got undefined error.
  3. home-controller_test.js: beforeEach usage is wrong. It expect a function
  4. home-controller_test.js: There is really no reason to add angular as dependency here.

You should see all these issues addressed in the angularAMD-karma project.

Answering your previous question, angularAMD is only required if you use it in the code you are trying to test. In the example of your home_controller, you do need to use angularAMD in order to property test your setup.

This also address #83.

mpiasta-ca commented 10 years ago

@marcoslin You were right about the angularAMD.inject() having to go inside the beforeEach(function () { }); bit.

I updated my runnable app to use the simplest solution possible. Some redundancies I found: