how does timeout work in angular tests running in karma

Problem

I love using console log for feedback perhaps too much, and sometimes I run into code that as convention we've added $timeout in the directive/service/controller, sometimes as long as 500 ms, and now the problem is during unit test, I noticed only console.logs directly under the it constructor gets sent out to karma and output to the screen.

wrapped console logs under timeout or rather wrapped assertions under $timeout do not yield any result as if ignored, what's the solution to timeouts?

Problem courtesy of: user2167582

Solution

In your unit tests, you load ngMock, which overwrites the orignal $timeout with its mock. Mock $timeout doesn't work like the real JavaScript timeout. To get it to call the code that's inside it, you have to do $timeout.flush() from your unit test.

If $timeout worked like the real timeout, you would've had to write asynchronous unit-tests for all functions that use $timeout.

Here's an example of a simplified function that uses $timeout and how I test it:

gaApi.getReport = function() {
  report = $q.defer()

  $timeout(function() {
    $http({method: 'GET', url: 'https://www.googleapis.com/analytics/v3/data/ga'})
      .success(function(body) {
        report.resolve(body)
      })
  }, 300)

  return report.promise
}

A unit test:

describe('getReport', function() {
  it('should return report data from Google Analytics', function() {
    gaApi.getReport().then(function(body) {
      expect(body.kind).toBe('analytics#gaData')
    })

    $timeout.flush()
    $httpBackend.flush()
  })
})
Solution courtesy of: M.K. Safi

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.