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.

argentum47
  • 2,385
  • 1
  • 18
  • 20
  • One approach would be to move the `myAppServices` into services folder like other service too. – Chandermani Apr 10 '15 at 06:43
  • myAppServices is my module name. it is in app/services https://github.com/argentum47/tmdb/tree/master/app/services . I am making use of the fact, once I have declared my dependencies like https://github.com/argentum47/tmdb/blob/master/app/services/movie.js , the next time I can just do https://github.com/argentum47/tmdb/blob/master/app/services/config.js – argentum47 Apr 10 '15 at 06: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 – Phil Apr 10 '15 at 06:50
  • As for your failing test, you need to use angular-mocks and mock out **all** the `$http` requests using `$httpBackend` – Phil Apr 10 '15 at 06:51
  • I think the [angular-phonecat repo](https://github.com/angular/angular-phonecat) uses the same structure as you mention here and use karma for testing, could be worth checking out. – jokarl Apr 10 '15 at 06:52
  • @hochas I did, and their tests pass mine doesn't. I will recheck. – argentum47 Apr 10 '15 at 06:53
  • @Phil I think I included angular-mocks in karam.conf.js. I think I should paste some part of the test and conf file in the question – argentum47 Apr 10 '15 at 06:54
  • 1
    I posted something about Angular module architecture. It should be helpful http://blog.inzynieria.it/getting-start-with-angular-require-grunt-bower-karma-seed – Łukasz Apr 10 '15 at 07:18
  • https://github.com/johnpapa/angular-styleguide here is John Papa's style guide - it's awesome – Callum Linington Apr 10 '15 at 07:24
  • thanks, I am looking into both of them. :) – argentum47 Apr 10 '15 at 07:26
  • Possible duplicate of http://stackoverflow.com/questions/29069261/karma-conf-js-automatic-file-ordering – dustin.schultz Jun 23 '15 at 17:28
  • @dustin.schultz interesting, that it didn't show up when I tried to search for the problem :P , thanks for the link though. – argentum47 Jun 23 '15 at 19:56
  • @argentum47, your question is interesting but your post is huge. It would benefit from being edited to get to the bottom of the problem… – Nowhere man Jun 25 '15 at 13:45
  • well if you could help with that only iff you have time. – argentum47 Jun 25 '15 at 14:55

2 Answers2

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.

Nowhere man
  • 5,007
  • 3
  • 28
  • 44
  • 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. – argentum47 Jun 25 '15 at 14:58
  • 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. – Nowhere man Jun 25 '15 at 15:17
  • See https://github.com/kephas/mdb/commit/00d1ef027aeee8bde4690e8871cdd5448121729c – Nowhere man Jun 25 '15 at 15:40
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

Community
  • 1
  • 1
argentum47
  • 2,385
  • 1
  • 18
  • 20