2

My unit tests are all failing with:

Error: Unexpected request: GET views/partials/listings.html`.

I've been reading through this SO question: Jasmine tests AngularJS Directives with templateUrl, but it appears that the discussion resolves this problem when using the ngMockE2E $httpBackend rather than the ngMock $httpBackend. I've tried to use the .passThrough() method that is mentioned in the accepted answer, but I get this error:

TypeError: '$httpBackend.whenGET('views/partials/listings.html').passThrough' is not a function`

It appears that the passThrough() method is only available on the ngMockE2E $httpBackend.

I wrote my test to mimic the phonecatApp XHR test from the AngularJS tutorial. This is the test that I'm working on:

(function() {
  'use strict';
  describe('Controller: ListingsCtrl', function() {
    var $httpBackend, ListingsCtrl, scope;
    $httpBackend = false;
    ListingsCtrl = false;
    scope = {};
    beforeEach(module('app'));
    beforeEach(inject(function(_$httpBackend_, $controller, $rootScope) {
      $httpBackend = _$httpBackend_;
      scope = $rootScope.$new();
      ListingsCtrl = $controller('ListingsCtrl', {
        $scope: scope
      });
      $httpBackend.expectGET('/api/listings/active').respond([
        {
          address: '123 Fake St'
        }, {
          address: '456 Other Ave'
        }
      ]);
    }));
    afterEach(function() {
      $httpBackend.verifyNoOutstandingExpectation();
      $httpBackend.verifyNoOutstandingRequest();
    });
    it('should have the correct default search parameters', function() {
      $httpBackend.flush();
      expect(scope.beds).toBe('Any');
      expect(scope.maxRent).toBe('None');
      expect(scope.search.address).toBe('');
      expect(scope.search.side).toBe('');
    });
    it('should have listings after loading them from the API', function() {
      expect(scope.listings.length).toBe(0);
      $httpBackend.flush();
      expect(scope.listings.length).toBeGreaterThan(0);
    });
  });

}).call(this);

Both of the tests fail in all four browsers that I test (Opera, Safari, Firefox and Chrome) with the same error message.

I was under the impression that unit testing with karma only loaded the controller code, and therefore wouldn't attempt to load any views or templates. Am I mistaken?

Community
  • 1
  • 1
Ben Harold
  • 6,242
  • 6
  • 47
  • 71
  • I have this problem as well "Unexpected request: GET", in my case another Service is being called within the Controller I'm testing. The Service is $translate and pulls in a translation .json file. – ericjam Sep 08 '15 at 15:03

1 Answers1

1

OK, I think there is great deal of confusion here, let me untangle this. Before diving into technical details here are some desirable properties of unit tests:

  • We want our unit-tests to be fast. Blazing fast. The reason is that we want to run unit tests often, very often (you do TDD, right?).
  • Unit testing setup should be as straightforward as possible. The reason is that we want the whole process to be as easy as possible. Otherwise people will tend to skip testing and we don't want bugs in our code.

Assuming that we want our tests to be easy to set up running fast you should not be serving templates via HTTP while testing directives with templates. Doing so will make the overall setup more complex (you need to setup a WWW server to serve templates, get the paths "right" etc.) and will slow down tests (additional, async HTTP request + network traffic while running tests).

What you should be doing instead is to preload directives' templates into $templateCache. The easiest way of doing this is to use the karma-ng-html2js-preprocessor. This pre-processor can get HTML files, stringify them as JS and put into $templateCache. If you are looking for a complete project setup with this approach, you can find an excellent example here: https://github.com/vojtajina/ng-directive-testing

To sum up: don't mix unit-testing and e2e mocks in your testing setup. This makes it more complex and slower to run. Preload directive templates into $templateCache instead avoiding any interactions with $http altogether.

pkozlowski.opensource
  • 117,202
  • 60
  • 326
  • 286
  • "don't mix unit-testing and e2e mocks in your testing setup" - can you please explain this more thoroughly? I have separate `protractor` e2e tests. I am following the AngularJS PhonecatApp as an example of how to setup unit tests, specifically `test/unit/controllersSpec.js` on step 5. Is this not a real unit test? I don't think I'm testing a directive with a template. I'm trying to test a controller. – Ben Harold Jul 27 '14 at 17:29
  • What I've mean about not mixing unit and e2e tests is not to mix them in the same karma run. Obviously you can have _separate_ runs for unit tests and e2e tests. When it comes to testing directives with templateUrl - this is in response to the other SO question you've linked from yours. – pkozlowski.opensource Jul 27 '14 at 18:03
  • I'm not attempting to mix unit tests and e2e tests. I'm attempting to unit test my controller. I can't figure out why `GET views/partials/listings.html` is even being called. I don't understand how or why any views or templates would be loaded during unit testing of a controller. – Ben Harold Jul 27 '14 at 19:37
  • @BenHarold in this case share the code of your controller (ideally the whole code + test in http://plnkr.co/) as it starts to get confusing what is your actual problem. – pkozlowski.opensource Jul 27 '14 at 20:01