0

I am unit testing an Angular controller which calls a service to get json data. I'm using Jasmine spyOn to spy on my service's query method like this:

spyOn facilitiesService, 'query'
    .and
    .callFake (success, error) ->
        deferred.promise.then success
        deferred.promise.catch error
        $promise: deferred.promise

I have defined a mock backend for running in the browser during development, that intercepts real REST calls, and uses $resource(path/to.json).query() to return fake data. Whenever I have the mock backend enabled, browser testing works fine, but my unit tests fail with unexpected request when I do scope.$digest(). How is the mock backend being called when I am spying on the method?

I would like this configuration to work with the mock backend specified so I can watch files and run unit tests before updating the application in the browser.

UPDATE:

http://plnkr.co/edit/hVc2YNnwUDNv7IHODOMD?p=preview

Here is a plunker I created that shows the behavior I'm seeing. Why is $httpBackend's whenGET method even being called?

I have seen other examples where they create a mock service that only contains empty methods to be spied on, but in that case, what is the point of spyOn's callFake if you already have a fake service, just put the callFake logic in the fake service's methods, and don't bother with spying.

Matt
  • 823
  • 1
  • 8
  • 17

1 Answers1

1

In your mock expectation you are responding with :

$resource('bagels.json2').query()

but that's the thing again thriggering http request. The option is to add one more expectation before it:

$httpBackend.whenGET('bagels.json2').respond(function() {return []});

http://plnkr.co/edit/dkwl51kdMk6dADWs10bZ?p=preview

After the comment below. Here is the solution where you should put expectation inside your test case.

http://plnkr.co/edit/YimHnl2KztI7GET2MNWw?p=preview

Also, you had (which was causing call to bagels.json2):

$httpBackend.whenGET('bagels.json').respond($resource('bagels.json2').query());

but you should have:

$httpBackend.whenGET('bagels.json').respond(function() {
  $resource('bagels.json2').query()
  });
Slaven Tomac
  • 1,552
  • 2
  • 26
  • 38
  • That will solve the problem for the test cases, but while running in the browser, I will never get any data correct? Is there a way to put the second .whenGET somewhere that will only be evaluated when a test is running? – Matt Mar 30 '16 at 13:11
  • You can put that inside your beforeEach statement, where it usually it should be. http://plnkr.co/edit/YimHnl2KztI7GET2MNWw?p=preview – Slaven Tomac Mar 30 '16 at 13:13
  • That suggestion works for me, but I'm still not understanding why the first whenGET is even being triggered in the first place. Isn't the spyOn ... and.callFake replacing the implementation of the query method? If so, what is causing whenGET to even be triggered? – Matt Mar 30 '16 at 13:15
  • Also, the thing is that you had defined respond wrong....you didn't wrap it inside the function (look at my example in beforeEach) and that's why it was trying to get bagels.json2. So definition, not calling of bagels.json caused that :) – Slaven Tomac Mar 30 '16 at 13:17
  • Got it now, I didn't realize respond() takes a function, so it was evaluating the $resource call immediately. – Matt Mar 30 '16 at 13:24
  • I think I am seeing issues with $httpBackend from ngMocks vs ngMockE2E. I am including ngMockE2E as a dependency to my app, which causes the browser to work correctly, but unit tests fail because of my original issue. If I remove the ngMockE2E dependency, and any httpBackend run blocks, unit tests run correctly. I think what I stumbled onto is I need a separate configuration for tests as I need for running in the browser. At least when running in the browser with a mock backend. – Matt Mar 30 '16 at 14:02