How do you test 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
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.
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:
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. 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]
You CAN call private methods!
If you encountered 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(/*...*/)
Thank @Moff452 for his/her comment. You can also write:
expect(new FooBar(/*...*/)['initFooBar']()).toEqual(/*...*/)
UPDATE:
@ts-expect-error
is a better alternative for @ts-ignore
. See: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#ts-ignore-or-ts-expect-error
as any
in that you lose any type-checking, actually you lose any type-checking on the entire line.
component.callPrivateMetod()
to component['callPrivateMethod]()
As most of the developers don't recommend testing 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 to @Aaron, @Thierry Templier.
interface Part { partNumber: string; }
test looks like this unpickPart(index: number, part: Part): void { //do stuff }
need to call unpick part some how.
This worked for me:
Instead of:
sut.myPrivateMethod();
This:
sut['myPrivateMethod']();
Do not write tests for private methods. This defeats the point of unit tests.
You should be testing the public API of your class
You should NOT be testing the implimentation details of your class
Example
class SomeClass {
public addNumber(a: number, b: number) {
return a + b;
}
}
The test for this method should not need to change if later the implementation changes but the behaviour
of the 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;
}
}
Don't make methods and properties public just in order to test them. This usually means that either:
You are trying to test implementation rather than API (public interface). You should move the logic in question into its own class to make testing easier.
call private method using square brackets
Ts file
class Calculate{
private total;
private add(a: number) {
return a + total;
}
}
spect.ts file
it('should return 5 if input 3 and 2', () => {
component['total'] = 2;
let result = component['add'](3);
expect(result).toEqual(5);
});
As many have already stated, as much as you want to test the private methods you shouldn't hack your code or transpiler to make it work for you. Modern day TypeScript will deny most all of the hacks that people have provided so far.
Solution
TLDR; if a method should be tested then you should be decoupling the code into a class that you can expose the method to be public to be tested.
The reason you have the method private is because the functionality doesn't necessarily belong to be exposed by that class, and therefore if the functionality doesn't belong there it should be decoupled into its own class.
Example
I ran across this article that does a great job of explaining how you should tackle testing private methods. It even covers some of the methods here and how why they're bad implementations.
https://patrickdesjardins.com/blog/how-to-unit-test-private-method-in-typescript-part-2
Note: This code is lifted from the blog linked above (I'm duplicating in case the content behind 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.format();
//...
}
}
class Address {
private format: StreetFormatter;
public format() {
//...
format.toString();
//...
}
}
class StreetFormatter {
public toString() {
// ...
}
}
A Note to Close On
You can implicitly test your private methods by making sure that conditions are met such that the code is called through the public interface. If the public interface does not call out to the private methods then that code is not providing any function and should be removed. In the example above there should be some effect that calling the private method should return ie: an object with an address. If there's not, for example the code emits an event in a private method, then you should start looking to decouple that so that it can be tested -- even in that example you would likely listen/subscribe to that event and would be able to test it that way. Decoupling leads to better testability and easier code maintenance later down the road as well.
The point of "don't test private methods" really is Test the class like someone who uses it.
If you have a public API with 5 methods, any consumer of your class can use these, and therefore you should test them. A consumer should not access the private methods/properties of your class, meaning you can change private members when the public exposed functionality stays the same.
If you rely on internal extensible functionality, use protected
instead of private
.
Note that protected
is still a public API (!), just used differently.
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 a consumer would use them, via subclassing:
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]);
});
Sorry for the necro on this post, but I feel compelled to weigh in on a couple of things that do not seem to have been touched on.
First a foremost - when we find ourselves needing access to private members on a class during unit testing, it is generally a big, fat red flag that we've goofed in our strategic or tactical approach and have inadvertently violated the single responsibility principal by pushing behavior where it does not belong. Feeling the need to access methods that are really nothing more than an isolated subroutine of a construction procedure is one of the most common occurrences of this; however, it's kind of like your boss expecting you to show up for work ready-to-go and also having some perverse need to know what morning routine you went through to get you into that state...
The other most common instance of this happening is when you find yourself trying to test the proverbial "god class." It is a special kind of problem in and of itself, but suffers from the same basic issue with needing to know intimate details of a procedure - but that's getting off topic.
In this specific example, we've effectively assigned the responsibility of fully initializing the Bar object to the FooBar class's constructor. In object oriented programming, one of the core tenents is that the constructor is "sacred" and should be guarded against invalid data that would invalidate its' own internal state and leave it primed to fail somewhere else downstream (in what could be a very deep pipeline.)
We've failed to do that here by allowing the FooBar object to accept a Bar that is not ready at the time that the FooBar is constructed, and have compensated by sort-of "hacking" the FooBar object to take matters into its' own hands.
This is the result of a failure to adhere to another tenent of object oriented programming (in the case of Bar,) which is that an object's state should be fully initialized and ready to handle any incoming calls to its' public members immediately after creation. Now, this does not mean immediately after the constructor is called in all instances. When you have an object that has many complex construction scenarios, then it is better to expose setters to its optional members to an object that is implemented in accordance with a creation design-pattern (Factory, Builder, etc...) In any of the latter cases, you would be pushing the initialization of the target object off into another object graph whose sole purpose is directing traffic to get you to a point where you have a valid instance of that which you are requesting - and the product should not be considered "ready" until after this creation object has served it up.
In your example, the Bar's "status" property does not seem to be in a valid state in which a FooBar can accept it - so the FooBar does something to it to correct that issue.
The second issue I am seeing is that it appears that you are trying to test your code rather than practice test-driven development. This is definitely my own opinion at this point in time; but, this type of testing is really an anti-pattern. What you end up doing is falling into the trap of realizing that you have core design problems that prevent your code from being testable after the fact, rather than writing the tests you need and subsequently programming to the tests. Either way you come at the problem, you should still end up with the same number of tests and lines of code had you truly achieved a SOLID implementation. So - why try and reverse engineer your way into testable code when you can just address the matter at the onset of your development efforts?
Had you done that, then you would have realized much earlier on that you were going to have to write some rather icky code in order to test against your design and would have had the opportunity early on to realign your approach by shifting behavior to implementations that are easily testable.
I agree with @toskv: I wouldn't recommend to do that:-)
But if you really want to test your private method, you can be aware that the corresponding code for the TypeScript correspond to a method of the constructor function prototype. This means that it can be used at runtime (whereas you will probably 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 transpiled into:
(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);
See this plunkr: https://plnkr.co/edit/calJCF?p=preview.
The answer by Aaron is the best and is working for me :) I would vote it up but sadly I can't (missing reputation).
I've to say testing private methods is the only way to use them and have clean code on the other side.
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 a lot of sense to not test all these methods at once because we would need to mock out those private methods, which we can't mock out because we can't access them. This means we need a lot of configuration for a unit test to test this as a whole.
This said the best way to test the method above with all dependencies is an end to end test, because here an integration test is needed, but the E2E test won't help you if you are practicing TDD (Test Driven Development), but testing any method will.
This route I take is one 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 breaking, but to answer the question, this is how I test private methods. I welcome anyone to advise on Pros & Cons of this.
Success story sharing
as any
: you lose all type-checking.