Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing private methods in JestJS

I am making an API with NestJS (using TypeScript) and it uses JestJS as the default test framework. I am writing a test for a service class and I am trying to access its private functions (Enforced with TypeScript), but I cannot for obvious reasons.

The traditional solution in other languages (like Java) is to change the functions to be in package or internal scope, but this doesn't exist in TypeScript.

How can I access those functions for testing purposes, but still enforce the private access (as good practice)?

like image 713
dicom-nate Avatar asked Mar 13 '19 17:03

dicom-nate


2 Answers

As a guideline, you should be able to test a class by using its public methods. If there is significant functionality hidden within private/protected access, then it might be a signal that it could be refactored out into another class.

Is it possible for you to refactor the complex private method(s) into another class(es) that you could then test separately?

Please bear in mind that testing your private methods will make your tests more fragile to changes in how you implement the behaviour of the class, even when the behaviour of the class itself hasn't changed. This is highly likely to be the case when refactoring for optimisation, for example.

Edit: Given that you’re working with JavaScript at the base level, there’s several ways you could achieve your goal, such as the solution you’ve posted making the methods protected and using a subclass to test, or by some variation of making the methods public, temporary or permanently, but it’s almost certainly unwise to do so in any form. I highly recommend reading the following link which provides further information about why it’s a bad idea to do so, and also provides potentially good solutions to the problems you’re having: https://enterprisecraftsmanship.com/2017/10/23/unit-testing-private-methods/

like image 163
adamfsk Avatar answered Nov 14 '22 19:11

adamfsk


My solution was to make the functions "protected" and extend the class in a class inside the test file, with those functions exposed.

Example:

class ClassToTest {
    protected add(x: number, y: number}: number {
        return x + y;
    }
}

In the test file:

class PublicClassToTest extends ClassToTest {
    public add(x: number, y: number): number {
        return super.add(x, y);
    }
}

describe('ClassToTest', () => {

    const obj = new PublicClassToTest();

    it('adding 2 numbers', () => {
        // GIVEN x is 6
        const x = 6;

        // AND y is 2
        const y = 2;

        // WHEN we add them
        const result = obj.add(x, y);

        // THEN the result is 8
        expect(result).toBe(8);
    });
});

As a bonus feature: This not only solved my issue with this, but also provided a nice way for me to see my function headers from within the test file, without needing to switch back and forth between my test and the class I am testing.

like image 40
dicom-nate Avatar answered Nov 14 '22 19:11

dicom-nate