Liza Shulyayeva

Exploring testing private TypeScript functions with Jest



I’ve been writing a bit of TypeScript lately, and have needed to learn some strategies to test private functions that are not class methods. With class methods it’s straightforward since you can use array access to get to them, but with functions it seemed a bit trickier.

This is not intended to be an argument for or against testing private functions. I know, I know, some people are deathly opposed to testing anything but the public API. This is one of those debates I’d have totally gotten into a few years ago and now just cannot be bothered. I have some testing approaches and preferences that usually work, but sometimes something else makes more sense and then I do that (or err with the standard if working in an existing codebase). In my opinion, there are valid cases for testing private functions.

Anyway, just thought I’d jot down a couple of notes after exploring different ways to test private TypeScript functions.

Conditional export for functions

I really wanted to avoid this; creating a special code path in production code just to allow a special testing use case feels dirty. But eventually I decided that maybe the simplest solution is the best one here ¯\(ツ)

somePrivateFunction(foo: string) {}

// testExports are only to be used for unit tests.
// They will throw an exception if used in production.
export const testExports = {
  somePrivateFunction: (foo: string) => {
    if (process.env.NODE_ENV !== 'test') {
      throw new Error("not permitted outside of test environment");
    }
    return somePrivateFunction(foo);
  },
};

This allows me to access somePrivateFunction() in my Jest test file via the testExports object, but prevents it from being accessed in production.

But inline tests?

By putting the test in the file where the private function is defined, the test can access it without needing its own special export. But this results in the test also being part of the final webpack build bundle. I think there must be a way I have not yet discovered to configure this away somehow. I wonder if I could add some kind of decorator to my inline test definitions to instruct them to be ignored at transpilation time?

I suppose another negative is that it seems like the most common way to organize Jest tests is in their own test files, either alongside the source on in a separate test directory. Putting tests inline might seem unconventional to other contributors or reviewers (but honestly I think I might prefer this approach to the above if I find a way to exclude them from the final build).

© 2022 · Liza Shulyayeva · Top · RSS · privacy policy