2017-04-01 74 views
1

這是我的測試:

it('add.user() should POST to /users/, failure', function() { 
    mockBackend.expectPOST("https://stackoverflow.com/users/", {username:'u', password: 'p', email: 'e', location: 'loc'}).respond(400, {msg: "bad request"}); 

    BaseService.add.user({username:'u', password: 'p', email: 'e', location: 'loc'}); 

    mockBackend.flush(); 
}); 

afterEach(function() { 
    mockBackend.verifyNoOutstandingExpectation(); 
    mockBackend.verifyNoOutstandingRequest(); 
}); 

當我運行這個測試,我得到這個錯誤:

Chromium 53.0.2785 (Ubuntu 0.0.0) Factory: BaseService add.user() should POST to /users/, failure FAILED 
    [object Object] thrown 
    Error: [$rootScope:inprog] $digest already in progress 
    http://errors.angularjs.org/1.3.15/$rootScope/inprog?p0=%24digest 
     at /home/user/Documents/ebdjango/ebdjangoapp/static/js/angular.js:63:12 
     at beginPhase (/home/user/Documents/ebdjango/ebdjangoapp/static/js/angular.js:14820:15) 
     at Scope.$digest (/home/user/Documents/ebdjango/ebdjangoapp/static/js/angular.js:14262:9) 
     at Function.$httpBackend.verifyNoOutstandingExpectation (node_modules/angular-mocks/angular-mocks.js:1557:38) 
     at Object.<anonymous> (tests/test_base.js:61:21) 

這是BaseService.add.user()

self.add = { 
    user: function(user) { 
    return $http.post("https://stackoverflow.com/users/", user) 
     .then(function successHandler(response) { 
      return $http.post("/custom-api-auth/login", user) 
     }).then(function successHandler(response) { 
      $window.location.href = "/"; 

    // if there are errors, rewrite the error messages 
    }).catch(function rejectHandler(errorResponse) { 
        for (prop in errorResponse.data) { 
          if (prop == "email") { 
           errorResponse.data[prop] = "Please enter a valid email address."; 
          } else if (prop == "username") { 
           errorResponse.data[prop] = "Username can only contain alphanumeric characters and '.'"; 
          } else if (prop == "password") { 
           errorResponse.data[prop] = "Please enter a valid password"; 
          } 
        } 
     throw errorResponse; 
}; 

如何防止$digest already in progress錯誤的發生?

編輯:如果我刪除throw errorResponse;,測試工作,但我需要throw errorResponse;那裏,因爲我需要顯示在前端的錯誤(這另一個控制器需要照顧.. BaseService.add.user().catch()基本上重寫應該在顯示的錯誤前端)。

編輯2:當錯誤消息指出at Object.<anonymous> (tests/test_base.js:61:21)它指向的行:mockBackend.verifyNoOutstandingExpectation();

+0

錯誤消息被截斷。請始終發佈它們。發佈的代碼中沒有任何內容會導致這種情況。該測試很可能與其他單位不夠隔離。如果應用程序中存在路由器,則應將其存起來。順便說一句,$ window.location本身是不好的,它會搞砸下一個測試,其中一個響應不會返回錯誤。 – estus

+0

@estus我編輯的職位,以顯示完整的錯誤消息。另外,如果不是'$ window.location'來設置URL,我應該使用什麼呢?(在測試時,我想將URL設置爲任何特定的URL,並驗證URL在POST成功後更改。在實際應用程序中 - 未測試時 - 我使用$ window.location將用戶重定向到不同的URL ) – user2719875

+0

你可以分享你的'mockBackend'嗎? – tanmay

回答

1

與上述代碼的問題是,它引發內部catch塊。與其他promise實現相反,在$ q中拋出和拒絕並不是一回事。

考慮到$ Q諾言鏈上消化($httpBackend.flush()這裏)執行同步,裏面catch塊扔將導致未捕獲的錯誤,而不是在拒絕承諾。它可能阻止摘要被完成,並於$摘要結果已經在接下來的消化進度錯誤。

所以一般$q.reject應該用於承諾預期的錯誤,而throw應該只用於嚴重錯誤。它應該是

return $q.reject(errorResponse); 

建議使用Jasmine promise matchers進行測試:

expect(BaseService.add.user({ ... }).toBeRejectedWith({ ... }); 

否則,它必須通過更復雜的諾言鏈進行測試:

var noError = new Error; 

BaseService.add.user({ ... }) 
.then(() => $q.reject(noError)) 
.catch((err) => { 
    expect(err).not.toBe(noError); 
    expect(err).toEqual(...); 
}); 

$rootScope.$digest(); 

位置變化應存根在測試中,因爲在測試中不斷變化的實際位置是過去的事情了一個需求:

module({ $window: { 
    location: jasmine.spyObj(['href']) 
} }) 

並且優選在角應用而不是直接訪問location使用$location服務,除非證明的,否則$location.path('/...')代替location.href = '/...'

它是使用在測試中已經安全的,雖然$location.path可額外窺探進行測試。

+0

只是一個FYI,我記得爲什麼我使用'$ window.location.href'而不是'$ location.path'。這是因爲我想徹底刷新網頁並使用'$ location.path'不刷新(它只改變URL)。 – user2719875

+0

是的,$ location.path不這樣做。通常,刷新頁面被認爲是指示設計問題的SPA中的破解。任何方式,測試這種情況都可以通過模擬$ window來實現,如後文所述(可測試性是$ window.location比全局位置更好的原因)。 – estus

+0

好的,謝謝(我在這個應用程序中需要整頁刷新的原因是因爲我遇到了這個問題:http://stackoverflow.com/questions/43218285/using-django-template-inheritance-with-ngroute-where-does -div-ng-view-go-i - 以防萬一您熟悉模板繼承併爲其提供解決方案)。 – user2719875