Testing private methods in Typescript
Tags: typescript, webdev, testing
Published: 2022-02-06 16:47:12

Sometimes we need to test a private method, and `Yes`, maybe it's not a *best practice*, but in my case have a situation when need adds a test to my private method and if you have the same situation I hope it helps you.

My example is a service with a private method for error, we want to be sure the error method private returns the message. 

```typescript
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';

@Injectable({ providedIn: 'root' })
export class ProductService {
  constructor(private httpClient: HttpClient) {}

  getProducts(): Promise<any> {
    return this.httpClient
      .get('/api/products')
      .toPromise()
      .then((response: any) => response.data)
      .catch(() => {
        this.handleError(new Error('Failed to get products'));
        return [];
      });
  }

  private handleError(error: Error): string {
    return 'Ups a error' ;
  }
}
```
Because it is private we can't test but we have two approaches to cover test the handleError method. 

- Change signature to protected.
- Use array access for the private members
- Extract the private logic to file and export. (Thanks to @[Lars Gyrup Brink Nielsen](@LayZee) )

## Change signature from private to protected.

We change the signature for our private method to protected it implies that the method is accessible only internally within the class or any class that extends it but not externally.

 The next step is to create an extended class and call the method from a public method, and we test the public implementation in our test.

```typescript
class ProductServiceExtend extends ProductService {
  constructor(httpClient: HttpClient) {
    super(httpClient);
  }
  handleErrorExtended(error: Error): string {
    return this.handleError(error);
  }
}
```

In our test instead, to call the original service original, we use the extended class and call the public implementation.

```typescript
  test('should return error message', () => {
    const errorMessage = new ProductServiceExtend(null).handleErrorExtended(
      new Error('Failed to get products')
    );
    expect(errorMessage).toBe('Ups a error');
  });
```
## Use array access for the private members

If you don't want or can change the signature then, we can use ['handleError'] to access it without changing the signature.

> Some linter rules don't like to give access to objects using a string literal.

```typescript
  test('should print error in console', () => {
    const spy = jest.spyOn(console, 'error');
    const errorMessage = new ProductService(null)['handleError'](
      new Error('Failed to get products')
    );
    expect(errorMessage).toBe('Ups a error');
  });
```

## Move the private function to file and export

A great and clean option is to move the private function into another file and export it into the service and the test. because It makes it so easy to test the private dependency.

```typescript
export function handleError(error: Error): string {
    return 'Ups a error';
  }
```
Next, import the function into the service and the test.
> Thanks to @[Lars Gyrup Brink Nielsen](@LayZee) )

## Summary

I show 3 options but in my opinion, moving the private function into a file and exports it is the **best one**, keep your code clean and maintainable. If you don't want to create an extra file you may declare it into the same file. 

Change the signature good alternative but, of course, require to create an extra class as sandbox `protected` and extending the class as a sandbox to work with it.

Pick the best for you :)