2015-07-21 62 views
10

我正在爲Typescript中的應用程序編寫Jasmine單元測試,並通過Resharper運行它們。據推測,如果處理程序拋出異常執行操作:Promise.catch()在AngularJS單元測試中沒有發現異常

describe("Q Service Test",() => { 
    var q: ng.IQService; 
    var rootScope: ng.IRootScopeService; 

    beforeEach(inject(($q, $rootScope) => { 
     q = $q; 
     rootScope = $rootScope; 
    })); 

    it("Caught exceptions are handled properly",() => { 
     var state = 'ok'; 
     q.when(1) 
      .then(() => { 
       throw new Error("test exception"); 
      }) 
      .catch(() => { 
       state = 'error'; 
      }); 

     rootScope.$digest(); 
     expect(state).toBe('error'); 
    }); 
}); 

然而,測試失敗:

​​

是不是因爲我的測試環境/工具,或一些奇怪的行爲我錯誤地使用承諾機制本身?

回答

13

你肯定使用的承諾機制錯誤,拋出一個用戶定義的throw語句不構成捕它作爲承諾拒絕妥善。由於$q文件中指出:

當比較deferreds /承諾 的try/catch /拋出熟悉的行爲,認爲拒絕在JavaScript中的關鍵字。 這也意味着,如果您通過承諾錯誤 回調「發現」錯誤,並且您希望將錯誤轉發給當前承諾的承諾,則必須通過返回 通過返回 拒絕來「重新拋出」錯誤拒絕。

它們類似但不等同,爲了捕獲用戶定義的throw語句,應該使用catch語句塊。雖然$q承諾應該只有catch拒絕承諾。因此,在你的情況下,返回被拒絕的承諾是處理該過程的正確方式,而不是拋出用戶定義的異常。

DEMO

JAVASCRIPT

describe('Q Service Test', function() { 

    var $q, 
     $rootScope; 

    beforeEach(inject(function(_$q_, _$rootScope_) { 
    $q = _$q_; 
    $rootScope = _$rootScope_; 
    })); 

    it('Rejected promises are handled properly', function() { 

    var state = 'ok'; 

    $q.when(1) 
     .then(function() { 
     return $q.reject('rejected'); 
     }) 
     .catch(function() { 
     state = 'error'; 
     }); 

    $rootScope.$digest(); 
    expect(state).toBe('error');  

    }); 

}); 

UPDATE:

爲什麼你的代碼具有這樣的瀏覽器,是因爲角度的$q實現使用try/catch聲明的原因塊處理承諾隊列。當任何回調拋出任何錯誤時,它自己捕獲錯誤,拒絕它作爲拒絕的原因,之後它使用$exceptionHandler來記錄錯誤。我建議你簡單地退回被拒絕的承諾。

至於爲什麼單元測試這樣的行爲的原因是因爲angular-mocks實施$exceptionHandler與實際應用的$exceptionHandler不同。前者創建一個具有不同模式的提供者,默認的angular-mocks實現使用rethrow模式,該模式繼而拋出異​​常而不是記錄它。如果您希望單元測試的行爲與默認應用程序的$exceptionHandler相同,則可以將模式設置爲'log'

DEMO

JAVASCRIPT

describe('Q Service Test', function() { 

    var $q, 
     $rootScope; 

    beforeEach(module('ng', function($exceptionHandlerProvider) { 
    $exceptionHandlerProvider.mode('log'); 
    })); 

    beforeEach(inject(function(_$q_, _$rootScope_) { 
    $q = _$q_; 
    $rootScope = _$rootScope_; 
    })); 

    it('Caught exceptions are handled properly', function() { 

    var state = 'ok'; 

    $q.when(1) 
     .then(function() { 
     throw new Error(); 
     }) 
     .catch(function() { 
     state = 'error'; 
     }); 

    $rootScope.$digest(); 
    expect(state).toBe('error');  

    }); 

}); 
+0

感謝您的詳細解答。還有一個問題 - 我的帶'throw'的版本怎麼只能通過測試,並且在瀏覽器中按預期工作,除了被傳遞到'.catch'塊? – Impworks

+0

你已經提到它**在瀏覽器**中按預期工作,那麼你可以顯示該部分的代碼? – ryeballar

+0

當然是:https://jsfiddle.net/eqf54zzm/ – Impworks

1

博客文章Using and chaining promises in AngularJS提到,當你拋出和異常它「也會觸發Angular的註冊異常處理程序」。所以我的猜測是Jasmine正在使用Angular的異常處理程序來偵聽異常。

你需要拋出一個異常,你可能只是做這樣的事情:

return q.reject(new Error("test exception")); 
+0

有明確'預期q.reject'作品版本。但是,實際測試的模型與框架無關,並且不依賴於Angular服務,僅在promise界面上。我寧願沒有這種依賴性的解決方案。 – Impworks