Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test or mock a not-exported function with Jest using TypeScript

Is there a way to test not-exported functions with Jest using TypeScript? I've seen SO answers recommending some libraries like rewire but it seems they are not really TypeScript compatible yet. The other way would be to export these private functions as well, but I think there must be a solution without exporting just for testing purposes.

The setup is the following. There are two functions, one is exported, one is not.

export function publicFunction() {
  privateFunction();
}

function privateFunction() {
  // Whatever it does
}

In my unit-tests there are two cases I wish to solve. Testing the privateFunction itself and mocking it for publicFunction tests.

I am using ts-jest to compile the typescript files when testing. The jest.config.json looks like

{
  "transform": {
    "^.+\\.(t|j)sx?$": "ts-jest"
  },
  "testRegex": "(/__tests__/.*|(\\.|/)(test|spec))\\.(tsx?)$",
  "moduleFileExtensions": ["ts", "tsx", "js", "jsx", "json", "node"],
  "testEnvironment": "node",
  "globals": {
    "ts-jest": {
      "tsConfig": "test/tsconfig.spec.json"
    }
  }
}

I am familiar with jest.fn() but I don't know how to overwrite or extract the private function. And an unit-test would be something like

import { publicFunction } from '../src';

describe('publicFunction', () => {
  it('should call "privateFunction" once', () => {
    // Overwrite privateFunction with jest.fn();

    publicFunction();

    expect(...).toHaveBeenCalled(1);
  });
});

Or to test the private function, for which importing it is not possible.

import { privateFunction } from '../src/index'

describe('privateFunction', () => {
  it(...
});

Any ideas or suggestions? Thanks!

like image 268
Felix Lemke Avatar asked Feb 14 '19 18:02

Felix Lemke


1 Answers

For testing non-exported functions with JEST and Typescript I have used Rewire. With little problem that we have to provide rewire a path of the TS build folder and not the .ts file path.

Following is my directory structure:

app
|---dist
    |---controllers
        |---api.js
    src
    |---controllers
        |---api.ts
        |---api.spec.ts

So for rewire I have to provide path of ./dist/controllers/api and not ./src/controllers/api

Here is my sample code:

import rewire from "rewire";

const api = rewire('../../dist/controllers/api');   // path to TS build folder

describe("API Controller", () => {
    describe('Fetch Route Params', () => {

        let fetchRouteParams: (url: string) => string;

        beforeEach(() => {
            fetchRouteParams = api.__get__('fetchRouteParams'); // non-exported function
        });

        it('should fetch route params from url', () => {
            const url = "https://example.com/foo/api/products";
            expect(fetchRouteParams(url)).toEqual('foo/api/products');
        });

        it('should not fetch route params from url', () => {
            const url = "";
            expect(fetchRouteParams(url)).toEqual('');
        });
    });
});

Even I have not came across some better solution apart from going with rewire. But it works for testing non-exported functions.

NPM Packages I have used:

npm i -D rewire
npm i -D @types/rewire

Hope it helps, Thanks.

like image 161
Zaki Mohammed Avatar answered Oct 28 '22 06:10

Zaki Mohammed