16

Previously when I was writing angular apps, I used to do

angular.module('ngApp', ['all', 'required', 'ng*', 'dependencies'])

in my app.js and then inside services/ and controllers, I could simply do:

angular.module('ngApp')

I have a repo to demonstrate that.

But then I saw the angular-seed/, the way implemented was,

in controllers/

angular.module('appControllers', ['dependencies'])...

in services/

angular.module('appServices', ['dependencies'])..

in app.js

angular.module('ngApp', ['ng*', 'appControllers', 'appSrvices'])..

I had no issue with design, infact I thought it was good, since evrything was dependency injected as well as modular.

I have a situation where I have a services/movie.js that has

angular.module('myAppServices', ['ngResource']).factory(..)

and services/config.js

angular.module('myAppServices').factory(..)

But while writing tests with karma and jasmine. In the karma.conf.js,

I had files: ['usual', 'bower_components/angular-*.js', 'app/services/**/*.js', '..']

but the problem was config.js got loaded before movie.js and there were errors, myAppServices is not loaded or mis-spelt.

The way I fixed it was I did:

files: ['..', 'app/services/movie.js', 'app/services/config.js']

I have set up a github repo for this too. Here is the controller test file and here is the karma.conf.

I want to know what can be the possible approaches to take such modular approach, without having to specify the order in which the files are to be loaded for my tests.

And this is my first unit test, and its failing:

Error: Unexpected request: GET https://api.themoviedb.org/3/configuration?api_key=2e329c92227ed8be07944ae447c9426f
Expected GET https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f

It would be helpful if I could get some help in fixing that too.

The test

describe('Controllers', function() {

  beforeEach(module('myApp'));
  beforeEach(module('myAppServices'));

  describe("MoviesCtrl", function() {
    var scope, ctrl, httpBackend;

    beforeEach(inject(function($httpBackend, $rootScope, _$controller_, Movie, Config) {
      httpBackend = $httpBackend;
      ctrl = _$controller_;
      scope = $rootScope.$new();
    }));

    it("should return a list of movies", function() {
      var data = {results: [{name: "Abc"}, {name: "Def"}]};

      httpBackend.
        expectGET("https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f").
        respond(data);
      ctrl('MoviesCtrl', { $scope: scope });
      httpBackend.flush()
      expect(scope.image).toEqual("https://api.themoviedb.org/3/");
    });
  });

});

conf. file

module.exports = function(config) {
  config.set({
    basePath: './',

    frameworks: ['jasmine'],

    files: [
      'app/bower_components/angular/angular.js',
      'app/bower_components/angular-mocks/angular-mocks.js',
      'app/bower_components/angular-resource/angular-resource.js',
      'app/bower_components/angular-route/angular-route.js',
      'app/services/movie.js',
      'app/services/config.js',
      'app/controllers/*.js',
      'app/app.js',
      'unit-tests/**/*.js'
    ],

    exclude: [
      'app/**/*.min.js'
    ],

    preprocessors: {
    },

    reporters: ['progress'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};

UPDATE

I have figured out the error in test, I had to mock the other http request for the configuration. thanks to @Phil.

This is my test now:

describe('Controllers', function() {
  beforeEach(module('myApp'));
  beforeEach(module('myAppServices'));

  describe("MoviesCtrl", function() {
    var scope, httpBackend;
    var config_data = { images: { base_url: "http://tmdb.com/t/p", backdrop_sizes: ["w300", "w500"]}},
        movie_data = {results: [{name: "Abc"}, {name: "Def"}]};

    beforeEach(inject(function($httpBackend, $rootScope, $controller) {
      httpBackend = $httpBackend;
      scope = $rootScope.$new();
       httpBackend.
        expectGET("https://api.themoviedb.org/3/configuration?api_key=2e329c92227ed8be07944ae447c9426f").
        respond(config_data);
      httpBackend.
        expectGET("https://api.themoviedb.org/3/movie/top_rated?api_key=2e329c92227ed8be07944ae447c9426f").
        respond(movie_data);
      $controller('MoviesCtrl', { $scope: scope });
    }));

    it("should return a list of movies", function() {
      expect(scope.image).toEqual({})

      httpBackend.flush();

      expect(scope.image.backdrop_size).toEqual("w300");
    });
  });

});

Although I am not sure if this is the right test to do :P . Something like a VCR would be helpful.

14
  • One approach would be to move the myAppServices into services folder like other service too. Commented Apr 10, 2015 at 6:43
  • myAppServices is my module name. it is in app/services github.com/argentum47/tmdb/tree/master/app/services . I am making use of the fact, once I have declared my dependencies like github.com/argentum47/tmdb/blob/master/app/services/movie.js , the next time I can just do github.com/argentum47/tmdb/blob/master/app/services/config.js Commented Apr 10, 2015 at 6:45
  • 1
    I would move the config stuff into it's own module. I like to stick with a one-module-per-file design. Makes testing easy Commented Apr 10, 2015 at 6:50
  • As for your failing test, you need to use angular-mocks and mock out all the $http requests using $httpBackend Commented Apr 10, 2015 at 6:51
  • 1
    I posted something about Angular module architecture. It should be helpful blog.inzynieria.it/… Commented Apr 10, 2015 at 7:18

2 Answers 2

1

Why use two separate files for 10 lines each? The purpose of writing code in separate files is to keep it understandable and maintainable. It would make sense to keep your module 'myAppServices' in one file.

If you really need to break down your code in multiple files, you should make use of dependency injection and make them each a separate module (see my patch against your repo). Then the order of loading stops being an issue.

Sign up to request clarification or add additional context in comments.

3 Comments

well these are 10 lines here becoz it's some vogus to-do like code, in the real projects, the one I am doing right now, those are more than 100 lines.. although I urge them to break it like in this case, the services, controllers etc as separate modules.. the problem was specific to testing. will try again soon.. thanks.
It is very good practice to provide a simplified code for the question, but make it clear it is not your final code, so we can understand what your real requirements are.
0

I still haven't found a angular-ish solution to this problem. Still there are two ways to deal with it.

  1. Using RequireJS as in @Lukasz 's blog post

and the second one is a dirty one, which I did,

  1. Wrote an _config.js file inside services/, controllers/, directives/, which has, for example angular.module('myAppServices', ['ngResource']) in services/,

    And in the karma.config.js I had to do files: ['app/services/_config.js', 'app/controllers/_config.js] . Although the problem still remains because I have to specify the order in which the two _config.js's are to be loaded.

Another way could be to have a single app/config.js file with,

(function() {
  angular.module('myAppServices', ['ngResource']);
  angular.module('myAppControllers', ['myAppServices']);
}());

and then do files: ['app/config.js', 'app/controllers/**/*.js', 'app/services/**/*.js'] in karma.conf.js

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.