Question

I'm rather new at both RequireJS and Jasmine so I'm having a little trouble setting up a few basic tests to start with. I've found a lot of info on how to set up the two together, and have it working. However I have an interesting problem I can't seem to fix.

Unfortunately I'm not sure the best way to word the problem so here's some code:

main.js:

require(['jquery', 'manipulate'], function($, Manipulate) {
  var manipulate = new Manipulate;
  $(document).on('change', 'td input', function() {
    manipulate.pushChange();
  });
});

manipulate.js:

define(function() {
  function Manipulate() {
    //
  };

  Manipulate.prototype.pushChange = function() {
    return true;
  };

  return Manipulate;
});

ManipulateSpec.js:

describe('Manipulate', function() {
  var manipulate;

  beforeEach(function() {
    var flag = false;

    // Load fixtures into the HTML
    loadFixtures('ManipulateFixture.html');

    // Require the manipulate.js file
    require(['jquery', 'manipulate', 'main'], function(jQuery, Manipulate) {
      manipulate = new Manipulate;

      // Define any spies
      spyOn(manipulate, 'pushChange');

      flag = true;
    });

    // Wait for manipulate.js to load before running tests
    waitsFor(function() {
      return flag;
    });
  });

  it('should call pushChange after changing a cell', function() {
    $('td input').eq(0).trigger('change');
    expect(manipulate.pushChange).toHaveBeenCalled();
  });
});

(Removed some extra code)

If I console.log inside Manipulate.pushChange, it is firing. The problem is, the Manipulate object that is being spied on isn't the same object that is passed as an argument in the main.js file. So adding manipulate.pushChange in my it() block makes the test pass.

I found an answer for Backbone.js apps, which calls delegateEvents. I'm not sure if there is a similar solution for vanilla Javascript, jQuery, etc. but I can't find one.

Is there a better way to structure my files, potentially putting my jQuery event handlers in the manipulate module? Or just a way to "copy" events between the two objects? I don't even believe using createSpy will help me much in this case.

Was it helpful?

Solution

There are some problems with your code that make it really hard to test. First you can't test requiereJs modules like the way you try it, if you wanna mock the dependencies. Take a look at this SO for some solutions.

The other problem is that you relay on jquery and DOM events. So most of the time I doesnt try to rebuild the DOM with fixtures for the test. Instead I spy on the jquery object that the event was binded to and call the function by myself. So in for your code

$(document).on('change', 'td input', function() {
  manipulate.pushChange();
});

you could spy on $like this

var documentSpy ={on:jasmine.createSpy()};
spyOn(window, "$").andReturn(event); // in a requireJs module mock it like in the SO I've mention above

Now when your code bind the event it will just call the spy and you can check if the event was binded correctly:

var callback = documentSpy.on.mostRecentCall.args[2]
expect(documentSpy.on).toHasBeenCalledWith('change', 'td input', callback);
// fire the callback
callback();
expect(manipulate.pushChange).toHaveBeenCalled();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top