Mocking – not testing – private functions in JavaScript

[ original gist 12 July 2013 ]

Instead of trying to extract a private function, we can rely on mocking/spying. This gist post shows how to use the Function() constructor to replace an internal call so that we can mock, spy or stub.

First Things

This is a followup to my (second) response at https://gist.github.com/dfkaye/5971486 to @philwalton, a suggestion for annotating functions to be extracted and tested separately (publicly). @philwalton’s original post, How to Unit Test Private Functions in JavaScript, shows how to use annotations around JavaScript fragments that expose otherwise private data publicly, but which are removed by a build process.

@philwalton very patiently listened to an unexpectedly opinionated JavaScript readership (who knew?) – for that I applaud him. I recommend you read his work regarding HTML Inspector as well as Object-oriented CSS, both of which I heartily endorse.

The mocking-not-testing alternative

If a function is public and uses another private function internally, we need a way to watch the public function’s behavior based on the result of its call to the private function.

How to do that using Function()

Starting with this:

var myModule = (function() {

  function foo() {
    // private function `foo` inside closure
    return "foo"
  }

  var api = {
    bar: function() {
      // public function `bar` returned from closure
      return foo() + "bar"
    }
  }

  return api
}())

Steps we’ll take:

  • create a mock foo function to be called in place of the real foo function
  • copy the source of the bar function under test
  • overwrite the inner function call foo() with "mockFoo()" inside the bar() function that we’re testing
  • create a new Function from the modified bar source
  • exercise the new bar function under test:

Details

Create a mock for foo:

function mockFoo() {
  return 'mockfoo'
}

Copy the source of bar:

var fn = myModule.bar.toString();

Overwrite the foo() call with mockFoo() in the new bar source text:

fn = fn.replace('foo', 'mockFoo');

Create a new Function from the modified bar source

// need to remove the open and close braces from the function source
fn = fn.substring(fn.indexOf('{') + 1, fn.lastIndexOf('}') - 1)

// create the new bar function
var bar = new Function(fn)

Exercise the new function under test:

assert(bar() === 'mockfoobar');

Closing statements

You could reassign the new bar function as myModule.bar directly, in the case there’s any this.something referencing going on.

This approach doesn’t test the private function directly, but does test the public function that relies on it. Therefore, it may help to add some test logic to the original bar function to verify that foo returns expected values, and that bar reacts as expected when foo returns something unexpected.

Metafunction

[ 27 Aug 2015 ] This line of thought developed into my own metafunction project on github.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s