Title: Javascript Named Exports for testing 
Slug: javascript-named-exports-for-testing
Date: 2022-01-09 13:15:00
Author: Kartones
Lang: en
Tags: Development, Testing, Javascript, Patterns & Practices, Typescript
og_image:
Description: Discusses a testing pattern using JavaScript named exports to test unexposed code fragments.

I'm now using [Jest](https://jestjs.io/) and its useful [mocking](https://www.npmjs.com/package/jest-mock-extended) [extensions](https://github.com/jest-community/jest-extended) quite often at work, and while the combination of modern Javascript + NodeJS+ Typescript + third-party packages + your code sometimes makes things quite a challenge to test, with effort (and a few refactors to ease testability) most times I feel satisfied with the ending results. 

Javascript imports via `import` ([MDN reference](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import)) are quite flexible, but on the other hand being *modules* means that you either export something, or you don't and then is not available from the outside, so it's not as easy to test the unexposed code fragments. A colleague at work explained to me a testing pattern for that scenario, but I haven't found it online, so this small post is both a personal note and a documentation attempt.

### The Testables Named Export Pattern

The main idea is using javascript named exports to export a specific group for testing with a meaningful name (e.g. `testables`). This way, you do add one additional `export`, but only one, also clearly signalling the intention; and you don't alter logic elsewhere in the code.

Let's use a tiny example:

`a-file.ts`
```
export function aMethod() { 
  ... 
  const result = aNonExportedMethod('something');
  anotherNonExportedMethod();
  ...
}

export function anotherMethod() { ... }

aNonExportedMethod(param : string) { ... }

anotherNonExportedMethod(); { ... }

export const testables = {
  aNonExportedMethod,
  anotherNonExportedMethod,
};
```

Your normal code will do normal imports, and simply won't care about `testables` nor be affected by it:

`another-file.ts`
```
import { aMethod, anotherMethod } from './a-file';

...
```

But when writing your tests, you will now be able to also import the `testables` group, so that you can write tests focused on that logic:

`a-file.test.ts`
```
import { aMethod, anotherMethod, testables } from './a-file';

...

const result = testables.aNonExportedMethod('a-string');
expect(result).toEqual(...);
```

### Closing Notes

I am assuming no default export, but the behaviour is pretty much the same otherwise. Just don't return the `testables` as the default export if you use it (I don't fancy much default exports, and seems [I'm not the only one](https://google.github.io/styleguide/jsguide.html#named-vs-default-exports)).


Be careful as module logic will still execute upon import (any Javascript/Typescript outside functions will run) no matter if you only `import { testables }`. This can bite you but in general it is a good practice to [avoid "import work"](https://dmitripavlutin.com/javascript-modules-best-practices/#2-no-work-during-import) so keep it in mind.

I know not everyone will like this approach and could argue that you should only test the visible surface (even more so if the non-exported methods are called inside the exported ones). But I at least prefer smaller, more focused tests so, in my example, after testing `testables.aNonExportedMethod` and `testables.anotherNonExportedMethod` the test(s) about `aMethod` would be much smaller and focused on the logic not already covered by others.
