marcoslin / angularAMD

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

"Cannot set property 'mock' of undefined" when using `app.requires.push('ngMock');` #114

Closed mpiasta-ca closed 9 years ago

mpiasta-ca commented 9 years ago

Hello again, I'm wondering if you have a suggestion for this case. I tried to push ngMock through the app_test.js file to avoid having to create a copy of app.js solely to use for testing, but I have some inconsistencies now.

PhantomJS seems to work, but when launching unit tests in other browsers (Chrome, Firefox, IE, Safari), it is very inconsistent experience. Tests will throw an error of either "Cannot set property 'mock' of undefined" or "angularAMD not initialized. Need to call angularAMD.bootstrap(app) first." In some browsers this happens all the time, in some it only shows on the first run, and on consecutive runs (while the karma server is already loaded from the first run) it won't throw the error again.

I tried several variations on the shim within main.conf.js and it doesn't seem to help. Here's my app_test.js file with the app.requires.push('ngMock') (is there perhaps an angularAMD equivalent for that function)?

app_test.js:

define(['app', 'angularAMD'], function (app, angularAMD) {
  //use angular-mocks
  app.requires.push('ngMock');

  describe('app.js', function () {
    console.log('### Running app_test.js: ');

    it('angularAMD must be bootstrapped', function (done) {
      var i = 0, retries = 5;
      var interval = setInterval(function () {
        var bootstrapped = true;
        i += 1;

        try {
          var a = angularAMD.appname();
        } catch (err) {
          bootstrapped = false;
          console.log("Waiting for angularAMD to boostrap, attempt " + i + ".  Error: " + err);
        }

        if (bootstrapped) {
          clearInterval(interval);
          console.log('### angularAMD bootstrapped.');
          done()
        } else {
          if (i > retries) {
            clearInterval(interval);
          }
        }
      }, 999)
    });
  });
});
marcoslin commented 9 years ago

I am not sure what you are trying to do. Why would you need to mock your app.js? The point of mock is to create an alternate version of something, but in this case, your app.js is already loaded at the define level.

What in app.js are you trying to avoid? Maybe I am missing something...

mpiasta-ca commented 9 years ago

1) So I'm trying to include "ngMock" because I'm mocking $http calls in the services/factory. ngMock has to be included as a dependency when the angular app is bootstrap, ie:

app.js

  var app = angular.module('app', ['ngMock']);
  return angularAMD.bootstrap(app);

However, since "ngMock" is only for unit testing, you do not want to actually load this in the production/development version of the app. So another way to inject it is to use angular.requires.push('ngMock'), which if I'm not mistaken, has to be included right after the angular app is bootstrap or it won't load properly.

So I'm trying to load ngMock within my app_test.js file, so that it can be used to mock $http requests in service/factory unit tests. This method has actually been working for me with PhantomJS no problem, but when I try to use this method with Chrome/Firefox/IE it throws an error.

2) Actually, IE (11.0.0) seems to be throwing an error every time now. Even when I use the normal way of injecting ngMock, all the other browsers are passing, but IE always fails with: angularAMD not initialized. Need to call angularAMD.bootstrap(app) first. So for some reason, that Jasmine 2 bootstrap setInterval code that you wrote isn't working with IE.

[Edit:] hmm actually I have some integrated tests which run IE fine, so I have no idea what's causing it to complain in my unit tests.