2017-04-01 69 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