Skip to content

Instantly share code, notes, and snippets.

@dfkaye
Last active March 13, 2019 15:40
Show Gist options
  • Save dfkaye/5987716 to your computer and use it in GitHub Desktop.
Save dfkaye/5987716 to your computer and use it in GitHub Desktop.
mocking - not testing - private functions in JavaScript, using new Function() - followup to https://gist.github.com/dfkaye/5971486

Mocking - not testing - private functions in JavaScript

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

Another response to @philwalton - 
http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/

This is a followup to https://gist.github.com/dfkaye/5971486 - a suggestion for 
*annotating* functions to be extracted and tested separately (publicly).

My first response - https://gist.github.com/dfkaye/5959089 - outlines testing a private 
function in a greenfield situation, before its embedding in a closure to be used by a
second public function.

First Things

@philwalton has 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 new Function()

  • 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

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
}())

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.

@dfkaye
Copy link
Author

dfkaye commented Dec 27, 2013

A complete solution to this problem here => https://github.com/dfkaye/metafunction

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment