javascriptunit-testingjasminejasmine-jquery

Jasmine: Testing a function, being called from a different function using jasmine


I am having a method in a javascript file.

function foo() {
  setTimeout(function() {
      bar.getSomeUrl();
      },WAIT_FOR_SOMETIME);
}

Now the getSomeUrl() is implemented as below.

var bar = {
     getSomeUrl : function(){
         window.location.href = 'someUrl';
         return;
     },
     anotherProp : function() {
         return bar.getSomeUrl();
     }
};

I am trying to test that the getSomeUrl() method will be called when I call the foo() method.

I am using jasmine for testing. My jasmine test is as below:

describe('This tests getSomeUrl()', function() {
    it('is called when foo() is called', function(){
        spyOn(bar,'getSomeUrl').and.callFake(function(){});

        window.foo();
        expect(bar.getSomeUrl).toHaveBeenCalled();

    });
 });

I don't really care about testing whats happening inside the getSomeUrl() because I have a separate test for that.

All I am trying to test is that when I call my foo() from somewhere the getSomeUrl() gets called.

I have the following problems:

  1. If I do this way, the test fails and at the end of running all tests, the browser redirects to someUrl. I didn't expect this to happen because, I thought since I had a spy on bar.getSomeUrl() and was returning a fake method it wouldn't actually call the bar.getSomeUrl() when I called window.foo().
  2. So I thought may be I should do it as below:

    expect(window.foo).toHaveBeenCalled();

This doesn't make sense, because I am trying to test that bar.getSomeUrl() is being called.

However when I did this, test fails and I get the below error:

Error: Expected a spy, but got Function.

I also thought it could be the setTimeout function that is causing the issue and changed the foo() function to:

function foo() {
    bar.getSomeUrl();
};

Didn't change anything

I have been working with Jasmine and Javascript only for a few days now and have a broad understanding of how things work.

Any suggestion to make this test to pass and also a pointer as to what I am doing wrong is greatly appreciated.


Solution

  • First, bar.getSomeUrl should be a function, not an (invalid) object

    var bar = {
         getSomeUrl : function() {
             window.location.href = 'someUrl';
             return;
         },
         anotherProp : function() {
             return bar.getSomeUrl();
         }
    };
    

    Second, use the Jasmine Clock when testing code with timeouts.

    describe('This tests getSomeUrl()', function() {
        beforeEach(function() {
            jasmine.clock().install();
        });
    
        afterEach(function() {
            jasmine.clock().uninstall();
        });
    
        it('is called when foo() is called', function(){
            spyOn(bar,'getSomeUrl').and.callFake(function(){});
    
            foo();
            expect(bar.getSomeUrl).not.toHaveBeenCalled();
    
            jasmine.clock().tick(WAIT_FOR_SOMETIME);    
            expect(bar.getSomeUrl).toHaveBeenCalled();    
        });
     });