Search code examples
javascriptunit-testingfunctional-testing

Unit testing javascript function that's unknowingly used elsewhere


I am just starting out with Unit Testing, so bear with me.

Take the following 'silly' example. I have created an object of elements on the page that I want to bind functions to. I set up a unit test for test.myFunction and ensure that it returns the 'textToReturn'.

var global = {
  o_bindings : [
    {
      object: '#element1',
      event: 'keyup',
      selector: '', 
      data: '',
      theFunction: function() {
        test.myFunction(textToReturn);
      }
    },

    // .... lots of other code ....

    {
      object: '#element2',
      event: 'keyup',
      selector: '', 
      data: '',
      theFunction: function() {
        test.myFunction(textToReturn);
      }
    }
  ]
};


$(document).ready(function() { 

  for (var i=0; i < global.o_bindings.length; i++) {
    var o = global.o_bindings[i];
    $(o.object).on(o.event, o.selector, o.data, o.theFunction);
  }
});

I revisit a year later, and decide that #element2 should return the same text every time, so I hard code it in the function and update that part of the original object:

    {
      object: '#element2',
      event: 'keyup',
      selector: '', 
      data: '',
      theFunction: function() {
        test.myFunction()
      }
    }

I've forgotten that the function is also used on #element1, and update my unit test accordingly, so that it always returns the same text.

The unit test would pass - but #element1 would not be working as expected.

Am I missing something? Or would this be picked up in functional testing, as it's out of scope for unit testing? Any advice would be greatly appreciated.


Solution

  • I'm not sure I exactly understand your code, but generally speaking a "unit test" should test the lowest level function possible. In this case, I would think that is test.myFunction(). So, if in the past it accepted an argument and then returned something based on that, and now it doesn't, then it doesn't really matter if you pass in anything. In other words, if the JavaScript function doesn't use the arguments at all, then passing them in (or not) won't matter (and thus will not affect the success of the tests.

    In your case (above), the use of test.myFunction(textToReturn) after the code change to no longer use the input doesn't really matter, since the method works fine either way. That said, let's assume you have some code in theFunction() which expected the proper textToReturn value, then you would write a test for theFunction() which checks this. Here's a simpler example of this:

    // sort of like your `test.myFunction()`
    function foo() {
        return "static value";
    }
    
    // the place where you remembered to change it...
    function bar() {
        $("#" + id).text( foo() );
    }
    
    // the place where you forgot to change it
    function baz(id) {
        $("#" + id).text( foo("some other value") );
    }
    

    For the source code above I would have tests for each function, both passing and those expected to fail. Here are some of the tests (there would be more):

    QUnit.test("make sure foo works", function(assert) {
        var v = foo();
        assert.equal(v, "static value", "The value returned by foo should be correct");
    });
    
    // I would have added this one after the change to your `test.myFunction()`
    QUnit.test("make sure foo works with input", function(assert) {
        var v = foo("some other value");
        assert.equal(v, "static value", "The value returned by foo does not change with input");
    });
    
    QUnit.test("make sure bar works", function(assert) {
        bar("theElementId");
        var t = $("#theElementId").text();
        assert.equal(t, "static value", "The value in the element should be correct");
    });
    
    /*
    // this is the ORIGINAL test for baz()
    // It would have failed after the code change
    QUnit.test("make sure baz works", function(assert) {
        baz("theElementId");
        var t = $("#theElementId").text();
        assert.equal(t, "some other value", "The value in the element should be correct based on input to foo()");
    });
    */
    
    // but I would CHANGE the test above to this:
    QUnit.test("make sure baz works", function(assert) {
        baz("theElementId");
        var t = $("#theElementId").text();
        assert.equal(t, "static value", "The value in the element should be correct");
    });
    

    Note that the fourth test would have failed, thus highlighting the fact that I had forgotten to update baz().