最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - $watch not firing when testing using Jasmine - Stack Overflow

programmeradmin2浏览0评论

I am trying to test my $watch listener, however, it's not firing in the test.

Here's the test:

it('should fire $watch when selectedBrands change', function() {
    spyOn($scope, 'updateFilters')

    $scope.selectedBrands = ['1'];

    expect($scope.updateFilters).toHaveBeenCalled();
})

And the controller:

$scope.selectedBrands = [];

$scope.$watch("selectedBrands", function() {
    $scope.updateFilters();
}, true);

Finally, the error:

Chrome 35.0.1916 (Mac OS X 10.10.0) ItemsController should fire $watch when selectedBrands change FAILED
    Expected spy updateFilters to have been called.
    Error: Expected spy updateFilters to have been called.
        at null.<anonymous> (/Applications/MAMP/htdocs/cloqet_client/testing/tests/ItemModule/ItemsController_spec.js:62:38)

I tried using $scope.$digest() but that makes the test pass even without changing $scope.selectedBrands.

Why is the $watch not being fired?

I am trying to test my $watch listener, however, it's not firing in the test.

Here's the test:

it('should fire $watch when selectedBrands change', function() {
    spyOn($scope, 'updateFilters')

    $scope.selectedBrands = ['1'];

    expect($scope.updateFilters).toHaveBeenCalled();
})

And the controller:

$scope.selectedBrands = [];

$scope.$watch("selectedBrands", function() {
    $scope.updateFilters();
}, true);

Finally, the error:

Chrome 35.0.1916 (Mac OS X 10.10.0) ItemsController should fire $watch when selectedBrands change FAILED
    Expected spy updateFilters to have been called.
    Error: Expected spy updateFilters to have been called.
        at null.<anonymous> (/Applications/MAMP/htdocs/cloqet_client/testing/tests/ItemModule/ItemsController_spec.js:62:38)

I tried using $scope.$digest() but that makes the test pass even without changing $scope.selectedBrands.

Why is the $watch not being fired?

Share Improve this question asked Jun 16, 2014 at 11:47 MaehMaeh 1,7744 gold badges18 silver badges31 bronze badges 1
  • Found anything interesting in the answers below ? – gkalpak Commented Jun 20, 2014 at 5:56
Add a ment  | 

3 Answers 3

Reset to default 3

It's correct to use $scope.$digest() to trigger the digest.

it('should fire $watch when selectedBrands change', function() {
    spyOn($scope, 'updateFilters')

    $scope.selectedBrands = ['1'];

    $scope.$digest();

    expect($scope.updateFilters).toHaveBeenCalled();
});

but that makes the test pass even without changing $scope.selectedBrands

The reason is $watch always fires the first time with undefined even you don't change the value. The current test reflects the correct behavior of your current code. It means that your code has a bug that needs to be fixed.

To fix this, try fixing your code:

$scope.$watch("selectedBrands", function(value) {
    if (value){ //check for undefined fired the first time.
       $scope.updateFilters();
    }
}, true);

DEMO

Tests run outside of the Angular-context, so there is no automatic $digest cycle happening.
$watch() is not fired because you need to first invoke a $digest cycle manually.

You should also keep in mind that every $watch callback is executed once to initialize the value.
That is why your $scope.updateFilter() gets called even without changing selectedBrands.
Quoting the docs:
"After a watcher is registered with the scope, the listener fn is called asynchronously (via $evalAsync) to initialize the watcher."

The proper way to test this, is to manually invoke a $digest cycle (so the the $watch is initiaized), then change selectedBrands and finally invoke a secod $digest cycle to "digest" that change.

E.g.:

it('should fire $watch when selectedBrands change', function() {
    $scope.$digest();

    spyOn($scope, 'updateFilters');
    $scope.selectedBrands = ['1'];
    $scope.$digest();

    expect($scope.updateFilters).toHaveBeenCalled();
});

See, also, this short demo.

you need a digest process to get fired, that's when watchers do checks

it('should fire $watch when selectedBrands change', function() {
    spyOn($scope, 'updateFilters')

    $scope.selectedBrands = ['1'];
    $scope.$digest()

    expect($scope.updateFilters).toHaveBeenCalled();
})
发布评论

评论列表(0)

  1. 暂无评论