jasmine jquery test to check if correct arguments have been passed to a method

newbie picture newbie · Oct 18, 2013 · Viewed 9.1k times · Source

I am new to javascript testing. I am using jasmine and need to test if correct arguments have been passed to a method.

This is my method:

    function myView(){
      if($('.view').is('.list')){
          myWindow('list');
      }else{
          myWindow('random');
      }
      $('.view').toggleClass('my-list');
    }

    function myWindow(list) {
      var url = /test.json;
      $.post(url, {"list": list});
    }

Here are my tests:

  describe('#myView', function() {
    beforeEach(function() {
      fixture.load('myview.html');
    });

    it('sets window to list', function(){
      expect(window.myWindow).toHaveBeenCalledWith('list');
    });
  });

I get the following error.

Error: Expected a spy, but got Function.

If i add this line before the expect(which seems wrong because I am specifying the correct param which should be identified by test)

spyOn(window, myWindow('list'));

I get the following error:

undefined() method does not exist

Can someone show my a good way to write the above tests?

Answer

Khanh TO picture Khanh TO · Oct 24, 2013

spyOn's second parameter is the name of the property you need to spy on. When you call spyOn(window, myWindow('list'));, your second parameter is the return value of myWindow('list') which is undefined => throws error: undefined() method does not exist

In your code, simply doing this should work:

describe('#myView', function() {
    beforeEach(function() {
      fixture.load('myview.html');
    });

    it('sets window to list', function(){
      spyOn(window, "myWindow");//spy the function
      myView();//call your method that in turn should call your spy
      expect(window.myWindow).toHaveBeenCalledWith('list');//verify
    });
  });

In software unit testing, there are concepts called stub and mock objects. These are dependencies of the method under test. spyOn is to create your fake objects to test your methods.

You're accessing the global window object directly, that's really a problem in unit testing. Although Javascript is a dynamically typed language, we're still able to mock your window object (this is not possible with some statically-typed languages like c#). But to create a good unit-testable code, I recommend you should redesign your code to inject it from outside.

function myView(awindow){ //any dependency should be injected, this is an example to inject it via parameter

      if($('.view').is('.list')){
          awindow.myWindow('list');
      }else{
          awindow.myWindow('random');
      }
      $('.view').toggleClass('my-list');
    }

Try this:

describe('#myView', function() {
    beforeEach(function() {
      fixture.load('myview.html');
    });

    it('sets window to list', function(){
      var spy = {myWindow:function(list){}};
      spyOn(spy, "myWindow"); //create a spy
      myView(spy); //call your method that in turn should call your spy
      expect(spy.myWindow).toHaveBeenCalledWith('list'); //verify
    });
  });

One more thing, jQuery code like this is not quite a good candidate for unit testing as it involves DOM manipulation in your code. If you have time, you should take a look at angularjs framework that separates your View (DOM) from your model (logic), uses dependency injection to make your code testable.