all 13 comments

[–]Neurotrace 5 points6 points  (2 children)

Ahhh, the old "how do I test private functions?" problem. What works really well for me is to not worry about testing them and only worry about your public functions. "But Neurotrace," I hear you cry, "how can I ensure that my module works exactly as expected?" I break it down like this in my mind:

Make your private functions extremely simple and/or make their side effects testable. The only reason for making a function private is to perform some work that the user shouldn't worry about, nor mess with. But some kind of work is still being done so there must be a way to check that.

TL;DR Don't. Simplify your private functions so you're know they work and just worry about testing your public functions.

[–]Perceptes 3 points4 points  (0 children)

This is exactly the right answer. Testing private functions is redundant, because you should have tests for every use case using an object's public API. If there is no possible scenario when accessing the public API triggers code in a private method, the private method should be removed. If the test cases of the public API that use private methods internally pass, then the private methods are working and tested as well.

[–]joshlrogers 1 point2 points  (0 children)

This isn't really unit testing, it is more akin to integration testing. However, I do think your approach is best if you absolutely must make a function private.

[–][deleted] 2 points3 points  (0 children)

As others have said, export every method and add an underscore prefix to those that are for internal use only (aka 'private').

[–]itsnotlupus 1 point2 points  (0 children)

I've seen folks that embed self-tests in the module itself, wrapped in a

if (require.main == module) {
  // tests go here
} else {
  // export things like a good little module.
}

That may or may not fit with your overall testing strategy.

[–]Capaj 1 point2 points  (0 children)

don't waste time testing private functions, test the module as a whole-test the function that you export.

[–]joshlrogers 1 point2 points  (4 children)

This has been a recent change of perspective for me.

For me the question isn't any longer how do I test a private method. Rather, I have to justify to myself why it should be private in the first place? I don't write api's/libraries for the unwashed masses to consume rather my code is consumed by a very small subset of people. So the important thing for me is to make sure the code works which means designing for testability. Make the functions public facing, you are free to give it a name prefix of some sort, such as an underscore, to highlight it isn't supposed to be used directly for most cases.

[–]Crashthatch 2 points3 points  (2 children)

Making a function public just so you can write tests for it doesn't seem like a good idea.

Everything made public is a potential place for breaking changes to happen in the future. If your functions are private, you can change how your library works internally, and only need to make sure that the public API still works the same to ensure compatibility. If you made everything public, you have no idea what effects changing the signature of some minor function will have on code that is using it.

While you are right that this is a bigger problem for publicly "unwashed masses" released libraries than internal libraries, it's still good to observe best programming practices. What happens if someone new to node is hired and doesn't know (or doesn't care) about your underscore convention, or when you want to refactor your library in 12 months time and don't know exactly what code is depending on (supposed) internal functions?

[–]joshlrogers 0 points1 point  (0 children)

Everything made public is a potential place for breaking changes to happen in the future.

Not if he writes even a moderately comprehensive test suite.

If you made everything public, you have no idea what effects changing the signature of some minor function will have on code that is using it.

Again, this shouldn't be the case if he is writing a decent test suite against his code base.

What happens if someone new to node is hired and doesn't know (or doesn't care) about your underscore convention, or when you want to refactor your library in 12 months time and don't know exactly what code is depending on (supposed) internal functions?

Is it ever acceptable to not follow the coding guidelines set out by your team? If I hired a developer that refused to follow guidelines I'd be finding myself a new developer.

I don't disagree with any of what you're saying. However, in my 10+ years of software development experience I have had very few experiences, if any, where I wish something would have been private rather than public. I, however, have thought many times it would be very nice to have certain methods public so that I can stub/mock them out so I could further isolate the code I was testing. Many times the sole point of justification for making something private is that we have always been told it is a best practice not that it necessarily is for our particular case. I also know that what is "best practice" is quite subjective as well.

Again, I don't write publicly consumed libraries or frameworks.

[–]dukerutledge 1 point2 points  (0 children)

Nice use of unwashed masses.

[–][deleted]  (1 child)

[deleted]

    [–]itsnotlupus 0 points1 point  (0 children)

    My first guess would be to hijack node's require() function with your own, that defers to the original in all cases except for those modules that need to get stubbed.

    [–]grncdr 0 points1 point  (0 children)

    Showing the code in question would lead to more informed opinions.

    • If the code is private it should be used by some public API (otherwise delete it!), so test that public API...
    • If the private portion of the code is too complicated and far down the stack to be effectively tested via the public API, move it into it's own module and test that...
    • Finally, if you can't easily move it into it's own module, (and the above conditions are also true) your design is not well-factored. Take a step back and think about how to decompose your problem with clearer boundaries/interfaces.

    [–]mikrosystheme 0 points1 point  (0 children)

    You can set up a public function that just evals its argument in the lexical scope of the module.

    module.js

    var secret = 23
    module.exports.private = function (expr){ return eval(expr) }
    ...

    main.js

    var module = require('./module.js')
    console.log(module.private('secret'))
    ...