snippettypescriptangularCritical
How to write unit testing for Angular / TypeScript for private methods with Jasmine
Viewed 0 times
angulartypescriptprivatewithhowjasminetestingmethodswritefor
Problem
How do you test a private function in angular 2 ?
The solution I found
-
Put the test code itself inside the closure or Add code inside the closure that stores references to the local variables on existing objects in the outer scope.
Later strip out the test code using a tool.
http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
Please suggest me a better way to solve this problem if you have done any?
P.S
-
Most of the answer for similar type of question like this one doesn't give a solution to problem, that's why I'm asking this question
-
Most of the developer say you Don’t test private functions but I don't say they are wrong or right, but there are necessities for my case to test private.
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
-
Put the test code itself inside the closure or Add code inside the closure that stores references to the local variables on existing objects in the outer scope.
Later strip out the test code using a tool.
http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
Please suggest me a better way to solve this problem if you have done any?
P.S
-
Most of the answer for similar type of question like this one doesn't give a solution to problem, that's why I'm asking this question
-
Most of the developer say you Don’t test private functions but I don't say they are wrong or right, but there are necessities for my case to test private.
Solution
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:
Even though TS restricts access to class members using
-
You can assert to
The problem with this approach is that the compiler simply has no idea what you are doing right of the
This will obviously make refactoring more difficult.
-
You can use array access (
While it looks funky, TSC will actually validate the types as if you accessed them directly:
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
The problem with this is, well, it suppresses all errors on the following line:
I personally consider
we recommend you use this comments very sparingly. [emphasis original]
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:-
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 errorThis will obviously make refactoring more difficult.
-
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 errorTo 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]
Code Snippets
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; }
}(thing as any)._name = "Unit Test";
(thing as any)._count = 123;
(thing as any).init("Unit Test", 123);(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 errorthing["_name"] = "Unit Test";
thing["_count"] = 123;
thing["init"]("Unit Test", 123);thing["_name"] = 123; // type error
thing["_count"] = "Unit Test"; // type error
thing["init"](0, "123"); // argument errorContext
Stack Overflow Q#35987055, score: 558
Revisions (0)
No revisions yet.