title: How to test an AngularJS watch using debounce function with Jasmine
body: <p>I have a controller with a watch that uses debounce from lodash to delay filtering a list by 500ms. </p>
  
  <pre><code>$scope.$watch('filter.keywords', _.debounce(function () {
    $scope.$apply(function () {
      $scope.filtered = _.where(list, filter);
    });
  }, 500));
  </code></pre>
  
  <p>I am trying to write a Jasmine test that simulates entering filter keywords that are not found followed by keywords that are found.</p>
  
  <p>My initial attempt was to use $digest after assigning a new value to keywords, which I assume didn't work because of the debounce.</p>
  
  <pre><code>it('should filter list by reference', function () {
    expect(scope.filtered).toContain(item);
    scope.filter.keywords = 'rubbish';
    scope.$digest();
    expect(scope.filtered).not.toContain(item);
    scope.filter.keywords = 'test';
    scope.$digest();
    expect(scope.filtered).toContain(item);
  });
  </code></pre>
  
  <p>So I tried using $timeout, but that doesn't work either. </p>
  
  <pre><code>it('should filter list by reference', function () {
    expect(scope.filtered).toContain(item);
    $timeout(function() {
      scope.filter.keywords = 'rubbish';
    });
    $timeout.flush();
    expect(scope.filtered).not.toContain(item);
    $timeout(function() {
      scope.filter.keywords = 'test';
    });
    $timeout.flush();
    expect(scope.filtered).toContain(item);
  });
  </code></pre>
  
  <p>I have also tried giving $timeout a value greater than the 500ms set on debounce.</p>
  
  <p>How have others solved this problem?</p>
  
  <p><strong>EDIT: I've found a solution which was to wrap the expectation in a $timeout function then call $apply on the scope.</strong></p>
  
  <pre><code>it('should filter list by reference', function () {
    expect(scope.filtered).toContain(item);
    scope.filter.keywords = 'rubbish';
    $timeout(function() {
      expect(scope.filtered).not.toContain(item);
    });
    scope.$apply();
    scope.filter.keywords = 'test';
    $timeout(function() {
      expect(scope.filtered).toContain(item);
    });
    scope.$apply();
  });
  </code></pre>
  
  <p><strong>I'm still interested to know whether this approach is best though.</strong></p>