Unit Testing of Private Methods

Should I test private methods or only public ones?

I do not unit test private methods. A private method is an implementation detail that should be hidden to the users of the class. Testing private methods breaks encapsulation.

If I find that the private method is huge or complex or important enough to require its own tests, I just put it in another class and make it public there (Method Object). Then I can easily test the previously-private-but-now-public method that now lives on its own class.

How do I test a class that has private methods, fields or inner classes?

If you have somewhat of a legacy Java application, and you're not allowed to change the visibility of your methods, the best way to test private methods is to use reflection.

Internally we're using helpers to get/set private and private static variables as well as invoke private and private static methods. The following patterns will let you do pretty much anything related to the private methods and fields. Of course, you can't change private static final variables through reflection.

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

And for fields:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);


Notes:

  1. TargetClass.getDeclaredMethod(methodName, argClasses) lets you look into private methods. The same thing applies for
    getDeclaredField.
  2. The setAccessible(true) is required to play around with privates.

Unit testing of private methods

If the methods are complex enough to warrant testing in isolation, then refactor them into their own class(es) and test via their public interface(s). Then use them privately in the original class.

How do you unit test private methods?

If you are using .net, you should use the InternalsVisibleToAttribute.

How to write unit testing for Angular / TypeScript for private methods with Jasmine

I'm with you, even though it's a good goal to "only unit test the public API" there are times when it doesn't seem that simple and you feel you are choosing between compromising either the API or the unit-tests. You know this already, since that's exactly what you're asking to do, so I won't get into it. :)

In TypeScript I've discovered a few ways you can access private members for the sake of unit-testing. Consider this class:

class MyThing {

private _name:string;
private _count:number;

constructor() {
this.init("Test", 123);
}

private init(name:string, count:number){
this._name = name;
this._count = count;
}

public get name(){ return this._name; }

public get count(){ return this._count; }

}

Even though TS restricts access to class members using private, protected, public, the compiled JS has no private members, since this isn't a thing in JS. It's purely used for the TS compiler. Therefor:

  1. You can assert to any and escape the compiler from warning you about access restrictions:

    (thing as any)._name = "Unit Test";
    (thing as any)._count = 123;
    (thing as any).init("Unit Test", 123);

    The problem with this approach is that the compiler simply has no idea what you are doing right of the any, so you don't get desired type errors:

    (thing as any)._name = 123; // wrong, but no error
    (thing as any)._count = "Unit Test"; // wrong, but no error
    (thing as any).init(0, "123"); // wrong, but no error

    This will obviously make refactoring more difficult.

  2. You can use array access ([]) to get at the private members:

    thing["_name"] = "Unit Test";
    thing["_count"] = 123;
    thing["init"]("Unit Test", 123);

    While it looks funky, TSC will actually validate the types as if you accessed them directly:

    thing["_name"] = 123; // type error
    thing["_count"] = "Unit Test"; // type error
    thing["init"](0, "123"); // argument error

    To be honest I don't know why this works. This is apparently an intentional "escape hatch" to give you access to private members without losing type safety. This is exactly what I think you want for your unit-testing.

Here is a working example in the TypeScript Playground.

Edit for TypeScript 2.6

Another option that some like is to use // @ts-ignore (added in TS 2.6) which simply suppresses all errors on the following line:

// @ts-ignore
thing._name = "Unit Test";

The problem with this is, well, it suppresses all errors on the following line:

// @ts-ignore
thing._name(123).this.should.NOT.beAllowed("but it is") = window / {};

I personally consider @ts-ignore a code-smell, and as the docs say:

we recommend you use this comments very sparingly. [emphasis original]

Should you cover private methods with a separate unit test

The idea is that your class exposes a an interface and your tests verify the input/output is correct from that interface. Each class/method should have a single responsibility.

The technique you described of marking a method as protected virtual is one that I have used myself on occasion, but this was within the context of a large legacy code application where it was impractical to refactor everything into a testable state right away. This technique should be used sparingly.

Generally if you feel that a private method is complex enough that it needs its own tests you should probably extract it out to its own class. If the private method does not have a lot of complexity then you will be needlessly asserting implementation details and making your code hard to refactor in future.

If you need to set up many mock objects to test a single class then it is probably violation SRP. One of the great side affects of writing tests is that it highlights any poorly designed classes.

I highly recommend this book to you: https://www.artofunittesting.com/

How to unit test private methods in Typescript

Technically, in current versions of TypeScript private methods are only compile-time checked to be private - so you can call them.

class Example {
public publicMethod() {
return 'public';
}

private privateMethod() {
return 'private';
}
}

const example = new Example();

console.log(example.publicMethod()); // 'public'
console.log(example.privateMethod()); // 'private'

I mention this only because you asked how to do it, and that is how you could do it.

Correct Answer

However, that private method must be called by some other method... otherwise it isn't called at all. If you test the behaviour of that other method, you will cover the private method in the context it is used.

If you specifically test private methods, your tests will become tightly coupled to the implementation details (i.e. a good test wouldn't need to be changed if you refactored the implementation).

Disclaimer

If you still test it at the private method level, the compiler might in the future change and make the test fail (i.e. if the compiler made the method "properly" private, or if a future version of ECMAScript added visibility keywords, etc).



Related Topics



Leave a reply



Submit