2

Here's how my function looks like.

var myFunc = function(){
   return functionReturningaPromise()
          .then(function(){
              //success, doesn't matter what happens here
          })
          .catch(function(err){
              //handle error here and then throw to handle higher
              throw new Error('Error in my function');
          })
}

I need the function to be this way to handle an error inside this function and then throw an error to handle on higher level. But i don't know how to test it with jasmine. I know how to control the promises for testing and my basic set up looks like this:

it('Should throw an error', inject(function(alert) {
    var instance = instanceFactory.createInstance(someData);
    var deferred = $q.defer();
    spyOn(someFactory, 'someMethod').and.returnValue(deferred.promise);

    //instance contains the throwing function above 

    instance.myFunc(otherData);

    deferred.reject({data: '12 - error'});
    $rootScope.$digest();

    expect(instance.myFunc).toThrow();

}));

Obviously, the error is not found by jasmine. So how to test the error throw in this case

Ivan
  • 16,536
  • 6
  • 23
  • 36

1 Answers1

2

$q does not work well with native throw, you should use $q API to re-throw or create new errors inside promise chain. Some Q/A to read about it:

The solution will be to use return $q.reject('Error in my function') instead of throw new Error('Error in my function');.

But the open question is how to test it. Basically you can make use of promise chains and add one more .catch() in test to check for error, and test is using Jasmine Async API:

it('should throw an error', function (done) {
// ----- use Jasmine async API -------^^^

    var instance = instanceFactory.createInstance(someData);
    var deferred = $q.defer();
    spyOn(someFactory, 'someMethod').and.returnValue(deferred.promise);

    // here we continue catching and check the error
    var promise = instance.myFunc(otherData);
    promise.catch(function (err) {
        expect(err).toBe('Error in my function');
        done();
    });

    deferred.reject({data: '12 - error'});
    $rootScope.$digest();
});

Here is a working sample (open script.js file in sidebar)

Community
  • 1
  • 1
Michael Radionov
  • 12,859
  • 1
  • 55
  • 72
  • #Michael Radionov I have a similar problem and your solution worked but I fear a false-positive: `function A() { try { B() } catch (error) { $q.reject(error); } //everything is fine $q.resolve(); function B() { throw ({myError: {error: 'error_message')}}}` The stranger thing is I've got the following error by applying your solution as-is: `Expected { myError : { error : 'error_message' } } to be { myError: { error : 'error_message' } }` But eventually, everything works when I move the `$rootScope.$digest` line **before** the `promise.catch etc` part – Gargaroz Jul 10 '17 at 15:10
  • 1
    Regarding error message - `toBe` matcher compares objects by reference, you should use `toEqual` to perform deep equality comparison. That is because you are using errors as objects vs errors as strings in my example. I am not sure about the digest though, it would be great if you shared your updated code in [plunker](http://plnkr.co/). – Michael Radionov Jul 10 '17 at 15:30
  • Sorry for the convoluted previous comment and thank you in advance for your help, [here's the plunkr](http://plnkr.co/edit/2z4LmCSpCL8eTYvPErKH) – Gargaroz Jul 10 '17 at 15:50
  • 1
    In the first example you call `$rootScope.$digest` before adding a catch handler, therefore the entire promise executes earlier than it gets to your catch - as a result test has **no** expectations and executes successfully. In the second example order is correct and error throws which is fine, but expectation fails because of the `toBe` vs `toEqual` difference I've explained above. Also, make sure to use Jasmine `done` for asynchronous test to spot false-positives, because it will explicitly wait for async expectations. [UPDATED PLUNKER](http://plnkr.co/edit/XtwQZ3Empa2eA3unWmRu?p=preview) – Michael Radionov Jul 10 '17 at 19:29
  • Thank you very much, I am unfamiliar with the done() practice because I'm using a 1.x jasmine version and I don't think I'm going to upgrade it right away: is there something I can do to mimic the done() mechanism? – Gargaroz Jul 11 '17 at 07:35
  • 1
    You should check [Asynchronous support](https://jasmine.github.io/1.3/introduction?#section-Asynchronous_Support) section in Jasmine 1.x docs, it's a bit more complex than `done`, but gets the job done. The point is that you have a custom `flag`, which is false by default, and Jasmine `waitsFor` it to become true after some async operation, after it becomes true, Jasmine executes the last `runs` block with expectations. – Michael Radionov Jul 11 '17 at 08:08
  • #Michael Radionov, I think I'm still doing something wrong, but for StackOverflow sake I asked [a new question](https://stackoverflow.com/questions/45058499/is-this-jasmine-asynchronous-unit-test-result-a-false-positive), could you please take a look at it? Thank you in advance – Gargaroz Jul 12 '17 at 13:08