I know that this is not necessarily the answer you are looking for, but I found that most of the time, if a private function deserves testing, it is in its own file.
For example, instead of using private methods in the same file as public ones like this ...
SIC / thing / PublicInterface.js
function helper1 (x) { return 2 * x; } function helper2 (x) { return 3 * x; } export function publicMethod1(x) { return helper1(x); } export function publicMethod2(x) { return helper1(x) + helper2(x); }
... you divided it like this:
SIC / thing / PublicInterface.js
import {helper1} from './internal/helper1.js'; import {helper2} from './internal/helper2.js'; export function publicMethod1(x) { return helper1(x); } export function publicMethod2(x) { return helper1(x) + helper2(x); }
SIC / item / internal /helper1.js
export function helper1 (x) { return 2 * x; }
SIC / thing / internal /helper2.js
export function helper2 (x) { return 3 * x; }
Thus, you can easily test helper1 and helper2 as it is, without using Rewire and other “magic” (which, as I discovered, has its pain points when debugging or when trying to switch to TypeScript, and not to mention the worst understandability for new colleagues). And the fact that they are in a subfolder with an internal name or something like that will help to avoid their accidental use in unintended places.
PS: Another common problem with "private" methods is that if you want to test publicMethod1 and publicMethod2 and make fun of helpers, then you usually need something like Rewire for this. However, if they are in separate files, you can use Proxyquire for this, which, unlike Rewire, does not require any changes in the build process, is easy to read and debug, and works well even with TypeScript.