I'm submitting a ...
Current behavior:
Starting in angular 1.6.0, when a unit test exercises an http call but doesn't specify an httpBackend expectation, neither the httpBackend.verifyNoOutstandingExpectation() nor the httpBackend.verifyNoOutstandingRequest() method throw an error. This problem still manifests in the latest snapshot.
Expected / new behavior:
Previously (on angular 1.5.11) each of the verify methods threw errors with the message "Unexpected request: GET
Minimal reproduction of the problem with instructions:
Write a unit test that exercises a $http call but don't tell httpBackend to expect it. Call httpBackend.verifyNoOutstandingExpectation() and httpBackend.verifyNoOutstandingRequest() and neither of them will throw an error and your test will pass.
Plunk on angular snapshot: http://plnkr.co/edit/DZZ2Nl?p=preview
Change the angular version to 1.5.11 and the test will pass.
Angular version: 1.6.3
This is an issue in 1.6.0 through the most recent snapshot of angular. An interesting note is that it is not the version of angular-mocks that makes the difference as I would have expected, but rather the version of core angular.
Browser: [all]
Happens in node/karma/jasmine tests and in Chrome jasmine runner (see plunk)
Anything else:
It looks like this is related to the error handler in your then function. If you remove the empty error handler, verify throws. Maybe because of https://github.com/angular/angular.js/commit/c9dffde1cb167660120753181cb6d01dc1d1b3d0
@gkalpak could this be related to the changes in the $q error handling?
Yes. When there is an unexpected request, $httpBackend throws, which causes the Promise returned by $http.get() to be rejected. Before 1.6, a thrown error was also passed to the $exceptionHandler, which rethrows by default in tests (hence you get an exception thrown). In 1.6. (due to e13eeabd7), thrown errors are treated the same as regular rejections, i.e. just reject the promise and don't get passed to the $exceptionHandler.
If there is an error handler somewhere down the promise chain, then the rejection is passed to that and the handler is responsible for taking over. If there is no error handler though, a Possibly Unhandled Rejection is passed to the $exceptionHandler (due to c9dffde), which again rethrows by default in tests.
If we want to restore the previous behavior (which sounds reasonable for the case where there is an unexpected or pending request), then we need to explicitly call $excptionHandler from the mock $httpBackend.
I would think this invalidates a lot of unit tests around the work that relied on this functionality. I know for us there are now a lot of test passing that should not be without anyone noticing.
This has forced us to downgrade to the previous version since our 4000 test suit suddenly is very lacking.
If anyone interested in taking a stab at it, please do :smiley:
something that seems to be closely related :
http://plnkr.co/edit/VMBlQVRLzExJVviBpDrs?p=preview
flash will fail, if there were not matching expect to the request
my bad, I've been just hitting the original issue
The unit tests that are meant to catch the issue:
https://github.com/marcin-wosinek/angular.js/commit/912175bc79c5a447d15c5984337cc6fc0393d0d3
Just running $exceptionHandler from the mocked $httpBackend (https://github.com/angular/angular.js/pull/16150/commits/8502070fff6694053718347c168205f0320390a0#diff-2a255ed5e9564e25ce6eb711b604f40fR1449) changes nothing - the handler just throws the same error, and seems that it's caught by $q try/catch anyway.
The PRed change is rather ugly (but it passes proposed tests); the possible improvement would be to have a specific error class that is rethrown by $q
I've tried $timeout/setTimeout tricks to throw the error outside the try/catch block, but it was ending to be thrown outside the test blocks too - rather unwanted behaviour, and difficult to be tested.
Most helpful comment
If we want to restore the previous behavior (which sounds reasonable for the case where there is an unexpected or pending request), then we need to explicitly call
$excptionHandlerfrom the mock$httpBackend.