theGreenCabbage
theGreenCabbage

Reputation: 4845

Jasmine unit test on Angular $timeout method

I have the following controller in my Angular application.

m = angular.module "myapp.dashboards"

    m.directive "lkDashboardElement", (
      $timeout
      MyAppSettings
    )->

      scope:
        dashboard: "="
        element: "="
        dashboardController: "="
        elementLoaded: "&"

      link: ($scope, $el)->

        if MyAppSettings.shouldCalculateTableWidth

          document.addEventListener "dashboard.element.rendered", =>

            $timeout(->
              ..
              ..
            )

I remove a lot of stuff so only the important part shows. The thing that I am having trouble with has to do with my usage of the Angular $timeout. I am currently checking for a certain condition shouldCalculateTableWidth, and if I see an event fire, I immediately timeout.

Currently I am trying to write a unit test that checks whether $timeout is being used.

Here is my test:

describe "in a phantomjs context", ->
  beforeEach ->
    # This sets our Phantom rendering context to true for testing purposes
    MyAppSettings._setIsPhantomRendering(true)

  afterEach ->
    MyAppSettings._setIsPhantomRendering(false)

  it "uses $timeout (instead of applyAsync) for adjusting table widths", ->
    # Creates a dummy dashboard
    dashboardController.queryMap = {1: {view: "foo", model: "bar"}}
    dashboard.elements = [{id: 1}]
    spyOn($timeout, "flush")
    expect($timeout.flush).toHaveBeenCalled()

What I am trying to do is simply test whether $timeout is being used in this piece of code, since it is important to how certain images are rendered when I am in Phantom (an image rendering library) context. When I run the test, I get the following error:

Expected spy flush to have been called.

The specific issue I have is the following two lines in my test:

spyOn($timeout, "flush")
expect($timeout.flush).toHaveBeenCalled()

First of all, I don't believe I am calling the right method for $timeout. It's very clear in my controller, I am calling $timeout, and not $timeout.flush. Second of all, for Jasmine Spys, you can't just spyOn the $timeout, since it needs both a reference to a class and a method.

So I am not quite sure how to move on. I would appreciate any help - thanks!

Upvotes: 1

Views: 1121

Answers (2)

Gaurav
Gaurav

Reputation: 1233

When you are writing the unit test, you have to call $timeout.flush() and then call $timeout.verifyNoPendingTasks();.

verifyNoPendingTasks() will throw an exception if there are any pending timeouts, so basically, you can assert that the exception is never thrown like expect(function () {$timeout.verifyNoPendingTasks()}).not.toThrow(). Also, you can write the expectation as expect(function () {$timeout.flush()}).toThrow()

If in your controller $timeout has a fixed time like $timeout(function() {}, 1000), then in your unit test you can flush as $timeout.flush(1000).

You can read more at here.

Also, you can take a look at the following CodePen for the working example.

Upvotes: 1

Amy Blankenship
Amy Blankenship

Reputation: 6961

If you want to spy on $timeout, you'll need to actually replace it in a module.decorator call with a spy. But you may want to ask yourself if it truly makes sense to micromanage the internals of your directive to that extent.

Upvotes: 0

Related Questions