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

How can you check a private function in angular 2?

class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar(); } private initFooBar(){ this.foo.bar( "data" ); this._status = this.fooo.foo(); } public get status(){ return this._status; } } 

The solution I found

Please suggest me the best way to solve this problem if you have done something?

PS

  • Most of the answer to a similar type of question like this does not solve the problem, so I ask this question

  • Most developers say that you do not test private functions, but I do not say that they are incorrect or correct, but for my case there is a need to check confidentiality.

+143
unit-testing angular typescript jasmine
Mar 14 '16 at 12:03
source share
10 answers

I’m with you, even if a good goal is “only unit testing the public API”, there are times when it does not seem so simple, and you feel like choosing between compromising the API or unit tests. You already know this, because that is what you are asking for, so I will not go into details. :)

In TypeScript, I found several ways to access private members for 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; } } 

Despite the fact that TS restricts access to class members using private , protected , public , compiled JS does not have private members, since this is not the case in JS. It is used exclusively for the TS compiler. Therefor:

  1. You can declare any and avoid the compiler, 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 that you are doing the right out of any , so you will not get the desired type of 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 
  2. You can use array access ( [] ) to access private members:

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

    While this looks funny, TSC will actually check the types as if you were accessing 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 obviously a deliberate "emergency hatch" to give you access to private users without losing security type. This is exactly what I think you want for your unit testing.

Here is a working example in TypeScript Playground .

+253
Mar 14 '16 at 15:24
source share

Since most developers do not recommend testing a private function , why not test it ?.

Eg.

YourClass.ts

 export class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar({}); } private initFooBar(data){ this.foo.bar( data ); this._status = this.foo.foo(); } } 

TestYourClass.spec.ts

 describe("Testing foo bar for status being set", function() { ... //Variable with type any let fooBar; fooBar = new FooBar(); ... //Method 1 //Now this will be visible fooBar.initFooBar(); //Method 2 //This doesn't require variable with any type fooBar['initFooBar'](); ... } 

Thanks @Aaron, @Thierry Templier.

+18
Mar 18 '16 at 6:54
source share

Do not write tests for private methods. This defeats the point of unit tests.

  • You must test your class public API
  • You should not test the implementation details of your class.

Example

 class SomeClass { public addNumber(a: number, b: number) { return a + b; } } 

The test for this method should not be changed if the implementation later changes, but the behaviour public API remains the same.

 class SomeClass { public addNumber(a: number, b: number) { return this.add(a, b); } private add(a: number, b: number) { return a + b; } } 

Do not use methods and properties publicly to test them. This usually means that:

  • You are trying to test an implementation, not an API (open interface).
  • You must move the logic in question to your class to make testing easier.
+6
Mar 14 '16 at 13:30
source share

You can call private methods. If you encounter the following error:

 expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/); // TS2341: Property 'initFooBar' is private and only accessible within class 'FooBar' 

just use // @ts-ignore :

 // @ts-ignore expect(new FooBar(/*...*/).initFooBar()).toEqual(/*...*/); 
+6
May 18 '19 at 1:39
source share

The point "do not test private methods" is actually a class test, like the one who uses it.

If you have an open API with 5 methods, any consumer of your class can use them, and therefore you should test them. The consumer should not have access to the private methods / properties of your class, that is, you can change the private members when the public functions remain unchanged.




If you rely on internal extensible functionality, use protected instead of private .
Note that protected is still a public API (!) , Just used in different ways.

 class OverlyComplicatedCalculator { public add(...numbers: number[]): number { return this.calculate((a, b) => a + b, numbers); } // can't be used or tested via ".calculate()", but it is still part of your public API! protected calculate(operation, operands) { let result = operands[0]; for (let i = 1; i < operands.length; operands++) { result = operation(result, operands[i]); } return result; } } 

Unit test protected properties in the same way as a consumer would use them through subclassification:

 it('should be extensible via calculate()', () => { class TestCalculator extends OverlyComplicatedCalculator { public testWithArrays(array: any[]): any[] { const concat = (a, b) => [].concat(a, b); // tests the protected method return this.calculate(concat, array); } } let testCalc = new TestCalculator(); let result = testCalc.testWithArrays([1, 'two', 3]); expect(result).toEqual([1, 'two', 3]); }); 
+4
Jul 01 '16 at 9:28
source share

Sorry for the necro in this post, but I feel compelled to weigh a couple of things that did not seem to be affected.

First of all, when we need access to private class members during unit testing, this is usually a large, thick red flag that we used in a strategic or tactical approach and inadvertently violated common responsibility mainly by pushing behavior when it is not belongs. Awareness of the need for access to methods that are actually nothing more than an isolated routine of the build procedure is one of the most common cases of this; however, it looks like your boss expects you to go to work getting ready for work, as well as with some perverse need to find out what morning routine you went through to get you into this state ...

The other most common case of this event is when you try to test the notorious "class of gods." This is a special problem in itself, but it suffers from the same main problem, when it needs to know the intimate details of the procedure, but this leaves the topic.

In this particular example, we effectively instructed to fully initialize the Bar object for the constructor of the FooBar class. In object-oriented programming, one of the main factors is that the constructor is "sacred" and must be protected from invalid data, which would deprive its own internal state and leave it loaded to fail elsewhere down (in what may be very deep pipeline.)

We could not do this here by allowing the FooBar object to accept a Bar, which is not ready at the time the FooBar was constructed, and received compensation by “hacking” the FooBar object with its own hands.

This is the result of refusing to join object-oriented programming (in the case of Bar) to another object, which means that the state of the object must be fully initialized and ready to immediately make any incoming calls to its "public users" after creation. Now this does not mean immediately after calling the constructor in all instances. When you have an object with many complex construction scenarios, it is best to expose the setters to their optional members, an object that is implemented in accordance with the creation design pattern (Factory, Builder, etc.). In any of the latter cases, you would click on the initialization of the target object to another graph of objects, the sole purpose of which is to direct traffic so that you get to the point where you have a valid copy of what you are requesting - and the product should not be considered " ready "until this creation object executes it.

In your example, the status property of Bar does not look like the correct state in which FooBar can accept it, so FooBar is doing something to fix this problem.

The second problem that I see is that it seems that you are trying to test your code, and not practice test-based development. This is definitely my own opinion at this point in time; but this type of testing is indeed an anti-pattern. What you end up doing is falling into the trap of realizing that you have problems with the underlying design that prevent your code from being checked after the fact, instead of writing the tests you need and then programming for the tests. In any case, you are faced with a problem, you should still have the same number of tests and lines of code, if you really achieved the implementation of SOLID. So - why try and reverse engineer your way into the code under test, when you can just solve this issue at the beginning of your development efforts?

If you did this, then you would understand much earlier that you would have to write some pretty bad code to test your design, and he would have the opportunity to rebuild your approach at an early stage, changing the behavior to implementations that are easy to verify .

+3
Dec 22 '17 at 6:08
source share

I agree with @toskv: I would not advise doing this :-)

But if you really want to test your private method, you may know that the corresponding code for TypeScript matches the prototype method of the constructor function. This means that it can be used at runtime (whereas you are likely to have some compilation errors).

For example:

 export class FooBar { private _status: number; constructor( private foo : Bar ) { this.initFooBar({}); } private initFooBar(data){ this.foo.bar( data ); this._status = this.foo.foo(); } } 

will be moved to:

 (function(System) {(function(__moduleName){System.register([], function(exports_1, context_1) { "use strict"; var __moduleName = context_1 && context_1.id; var FooBar; return { setters:[], execute: function() { FooBar = (function () { function FooBar(foo) { this.foo = foo; this.initFooBar({}); } FooBar.prototype.initFooBar = function (data) { this.foo.bar(data); this._status = this.foo.foo(); }; return FooBar; }()); exports_1("FooBar", FooBar); } } })(System); 

Check out this plan: https://plnkr.co/edit/calJCF?p=preview .

+2
Mar 14 '16 at 13:19
source share

As many have already said, if you want to test private methods, you do not have to crack your code or transporter for it to work for you. Modern TypeScript will deny most of the hacks that people have provided so far.




Decision

TL; DR ; if the method needs to be tested, then you must separate the code from the class so that you can provide the method for public testing.

The reason you have the private method is because the functionality does not have to be provided by this class, and therefore, if the functionality does not belong there, it should be divided into its own class.

example

I came across this article which explains how you should test private methods. It even discusses some of the methods and why they are poor implementations.

https://patrickdesjardins.com/blog/how-to-unit-test-private-method-in-typescript-part-2

Note This code is taken from the blog, to which there are links above (I duplicate in case the content of the link changes)

Before
 class User{ public getUserInformationToDisplay(){ //... this.getUserAddress(); //... } private getUserAddress(){ //... this.formatStreet(); //... } private formatStreet(){ //... } } 
After
 class User{ private address:Address; public getUserInformationToDisplay(){ //... address.getUserAddress(); //... } } class Address{ private format: StreetFormatter; public format(){ //... format.ToString(); //... } } class StreetFormatter{ public toString(){ // ... } } 
+1
Jun 26 '18 at 22:48
source share

Aaron ’s answer is the best and works for me :) I would vote, but unfortunately I can’t (there is no reputation).

I have to say that testing private methods is the only way to use them and have clean code on the other hand.

For example:

 class Something { save(){ const data = this.getAllUserData() if (this.validate(data)) this.sendRequest(data) } private getAllUserData () {...} private validate(data) {...} private sendRequest(data) {...} } 

It makes sense not to test all these methods at the same time, because we would need to model those particular methods that we cannot model, because we cannot access them. This means that we need a lot of configuration for unit testing in order to test this as a whole.

At the same time, the best way to test the method described above with all the dependencies is through testing, because an integration test is needed here, but the E2E test will not help you if you practice TDD (Test Driven Development), but you will test any method.

0
Aug 30
source share

I chose this route where I create functions outside the class and assign the function to my private method.

 export class MyClass { private _myPrivateFunction = someFunctionThatCanBeTested; } function someFunctionThatCanBeTested() { //This Is Testable } 

Now I don’t know what type of OOP rules I am violating, but to answer the question, I check private methods. I welcome anyone who advises for and against this.

0
Nov 15 '18 at 13:43
source share



All Articles