测试`catch()`错误处理程序

时间:2017-04-01 22:15:21

标签: angularjs jasmine karma-runner karma-jasmine angular-mock

这是我的测试:

it('add.user() should POST to /users/, failure', function() {
    mockBackend.expectPOST("/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("/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;因为我需要在前端显示错误(另一个控制器负责... {{1} }基本上重写应该在前端显示的错误。)

编辑2:当错误消息显示BaseService.add.user().catch()时,它指向该行:at Object.<anonymous> (tests/test_base.js:61:21)

1 个答案:

答案 0 :(得分:1)

上面代码的问题是它会抛出$httpBackend.flush()块。与其他承诺实现相反,在$ q中抛出和拒绝并不是一回事。

考虑到同步在摘要(catch)上执行$ q promise链,抛出$q.reject块将导致未被捕获的错误,而不是被拒绝的承诺。它可能会阻止摘要完成,并导致下一个摘要中的 $ digest已在进行中错误。

因此,一般throw应该用于承诺中的预期错误,而return $q.reject(errorResponse); 应该仅用于严重错误。它应该是

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

建议使用Jasmine promise matchers对其进行测试:

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

最好在Angular应用中使用location服务,而不是直接访问$location.path('/...'),除非另有证明,location.href = '/...'代替$location.path

在测试中使用已经安全了,虽然total = total + x; 可以另外进行测试。