10 JUL 2013
Start with the annotated example, from http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/ by @philwalton
This is an alternative response. The previous TDD-with-greenfield response is here => https://gist.github.com/dfkaye/5959089
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"
}
}
/* test-code */
api._foo = foo
/* end-test-code */
return api
}())
In Phil's original post, the annotation comments surround the api._foo
assignment,
which exposes foo for testing but marks this statement for removal by the build
tool that strips test-code annotations from the source AFTER tests, and BEFORE
copy+deploy.
I objected to the use of these annotations around temporary code. Not a terrible
offense but source modification seems an extra step I have to remember to annotate.
Lucky for us the build tool removes these.
The larger question, though, is how to access the private function another way.
Here I think the annotation approach can be used but in a different way - probably
more cumbersome to get working in a build tool, but once done would be portable
enough for re-use in the ecosystem.
In this case we start with the anonymous function. The first step is to name it or annotate it with something like "closure." Next, apply the build tool to scan files for these named functions, extract the function body, and write that to a closure test file directory. That will make the function's content global so there may be naming issues, but at least we won't be erasing anything from the source. Finally, execute the corresponding test/spec against this file.
The new source would be:
var myModule = (function closure() {
function foo() {
// private function `foo` inside closure
return "foo"
}
var api = {
bar: function() {
// public function `bar` returned from closure
return foo() + "bar"
}
}
return api
}())
And the extracted source to test against would be:
function foo() {
// private function `foo` inside closure
return "foo"
}
var api = {
bar: function() {
// public function `bar` returned from closure
return foo() + "bar"
}
}
The tests can still execute against foo()
and api.bar()
.
David, this is a pretty interesting solution, but I fear it might require an entire JS parser to make it work. There's also the naming conflict issue that you mentioned.
I think we both agree that the ideal solution would be to write your code the way you want to write it and then use a tool to allow you to test your code as thoroughly as you want.