Search code examples
javascriptjqueryjasminejasmine-jquery

How do you test a jQuery plugin if all methods are private?


Here is an example of a simple jQuery plugin:

it takes an element and if it is a div sets a width

;(function ($, window, document, undefined) {
  var pluginName= 'changeDivWidth',
      defaults = {
        width: '50%'
      };

  function Plugin (element, options) {
    this.element = element;
    this.options = $.extend( {}, defaults, options );
    this._defaults = defaults;
    this._name = pluginName;
    this.init();
  }

  Plugin.prototype = {
    init: function () {
      if(this.isDiv()) {
        this.changeWidth();
      }
    },
    isDiv: function () {
      if (this.element.nodeName === 'DIV') {
        return true;
      }
      return false;
    },
    changeWidth: functioon () {
      $(this.element).css('width', this.options.width);
    }
  };

  $.fn[ pluginName ] = function ( options ) {
    return this.each(function() {
      if ( !$.data( this, "plugin_" + pluginName ) ) {
        $.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
      }
    });
  };
}(jQuery, window, document));

How would I test this jQuery plugin?

My first reaction would be that I want to unit test each method of Plugin.prototype, but these are private and "you're not supposed to test private methods". So my only other option that I can see would be to run a bunch of integration tests where the plugin is called in different situations and see what the output is.

So say I want to test my two privte methods:

describe('changeDivWidth', function () {
  beforeEach(function () {
    loadFixtures('myFixtures.html');
  });

  it('should not change width of non-DIV elements', function () {
    var span = $('span').changeDivWidth({width: '30px'}).get(0);
    expect($(span).width()).not.toBe(30);
  });

  it('should change width of DIV elements', function () {
    var div = $('div').changeDivWidth({width: '30px'}).get(0);
    expect($(div).width()).toBe(30);
  });

});

Great, this seems to work... Except, if Plugin.prototype.isDiv fails in such a way that it always returns false then the test for Plugin.prototype.changeWidth will also fail. So now I have one failing method but two failing tests, meaning I don't know where the issue is. Obviously on something this small it's no big deal, but on a larger project I can see this becoming a pain.

So am I missing something here? Is there a better way to be testing this?

I suppose I could order my tests to run in the same order as the methods they are testing, but as the project grows in complexity I could see this being a pain to maintain.

Sorry for the long winded question. I wasn't sure how to ask it without examples.

P.S. I know my tests don't really hold up, they are just supposed to be an oversimplified example.


Solution

  • In my opinion, don't test the private methods. You should be testing against the public interface to make sure that it works as it should, and not worry about the internals because they should be able to change freely because they are private.

    On the point of small projects versus big ones: As projects become bigger, components within them should be broken up for the code to stay maintainable. Therefore, functions such as isDiv would probably be put into a utility class/object that can be used in many objects instead of having many copies of it in many different classes. When it is separated out like this, the function will be exposed in a public interface and therefore be testable.

    See: https://softwareengineering.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods