How do I expect an HTTP request NOT to be made?
Asked Answered
A

4

9

Angular's $httpBackend service lets you expect an HTTP request with expectGET, expectPOST, etc. (or just expect).

How would I write a test that says, "the controller should NOT make a request to this endpoint (under these conditions)"?

I was thinking something like:

$httpBackend.when('/forbidden/endpoint').respond(function() {
  throw Error("Shouldn't be making a request to /forbidden/endpoint!");
});

That seems a bit hacky to me, but I'm fine with it if that's the normal way to do things. (But I doubt that.)

Adams answered 14/3, 2014 at 15:56 Comment(2)
I'm still looking for a solution. Did you find one?Thales
I thought about it and I think I found a good solution I guess. Answer is followingThales
T
12

I stumbled over the same issue.

The solution would be to have a callback function as response and inside you could expect(true).toBe(false) or in my opinion something a little bit more beautiful:

it ('should not trigger HTTP request', function() {
    var forbiddenCallTriggered = false;
    $httpBackend
      .when('/forbidden/endpoint')
      .respond(function() {
        forbiddenCallTriggered = true;
        return [400, ''];
      });

    // do whatever you need to call.

    $rootScope.$digest();
    $httpBackend.flush();

    // Let test fail when request was triggered.
    expect(forbiddenCallTriggered).toBe(false);
  });
Thales answered 10/9, 2014 at 11:19 Comment(3)
When running that I get a Error: No pending request to flush ! errorJusticz
$httpBackend.verifyNoOutstandingRequest might help achieve what you needGrissel
Using $rootScope.$digest() without $httpBackend.flush() solved the No pending request to flush error for me.Rousing
K
5

For scenarios like this I often use Jasmine's spyOn() function. You can spy on functions of $http, $resource, or of a custom service (like myServiceThatUsesHTTP below):

spyOn(myServiceThatUsesHTTP, 'query');
// test, then verify:
expect(myServiceThatUsesHTTP.query).not.toHaveBeenCalled();
// or
expect(myServiceThatUsesHTTP.query.callCount).toBe(0);

When you spyOn() a function, the original function is replaced. The code for the original function is not executed, which can be good or bad (depending on what you need to do for the test).

For example, if you need the $promise object that $http or $resource returns, you can do this:

spyOn($http, '$get').andCallThrough(); 
Kryska answered 14/3, 2014 at 16:9 Comment(3)
I considered this as well, but it seems a bit fragile, doesn't it? It might happen that the calling code subtly changes so not exactly the same method is called, but the HTTP request still gets made. Or maybe that's a contrived scenario that wouldn't actually happen; I don't know.Adams
Yeah good point. I don't think it's necessarily a contrived scenario. I'm coding such a test right now and now you've got me thinking twice :)Kryska
To me I consider your solution safe enough ;) Just a typo correction anyway, it's ".and.callThrough();" rather than camel case "andCallThrough()"Patsis
T
2

One solution might be to check if $httpBackend.flush() throws an exception, since there should be nothing to flush:

beforeEach(function() {
   $httpBackend.whenGET('/forbidden/endpoint');
   ...
   // call service method under test (that should not make $http call)
});

it('Should not call the endpoint', function() {
    expect($httpBackend.flush).toThrow();
});

Important thing to note: we use when and not expect, since we don't actually expect the call to be made. And since there is no call, $httpBackend.flush() will throw an exception: No pending request to flush.

Tricorn answered 10/1, 2017 at 12:57 Comment(0)
T
0

$httpBackend is not applied because the $http call doesn't get made in this test.

Instead, you can inject $http in your test, and then spyOn() $http directly:

beforeEach(fn () { 
  inject(function ($injector) {
    this.service = $injector.get('serviceOrControllerOrDirectiveBeingTested');
    this.$http = $injector.get('$http');
  }
});

and then

it('should ...', fn() {
  spyOn(this.$http, 'get');
  this.service.methodThatTriggersTheHttpCall();
  expect(this.$http.get).not.toHaveBeenCalled();
});
Trenchant answered 13/11, 2015 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.