Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Test an Angular2 Pipe that has a constructor method in Jasmine

I am falling at the first hurdle testing an Angular Pipe that has a constructor.

My Pipe is as follows:

reverse.pipe.ts

import {
  IterableDiffer,
  IterableDiffers,
  Pipe,
  PipeTransform
} from '@angular/core';

@Pipe({
  name: 'reverse',
  pure: false
})
export class ReversePipe implements PipeTransform {

  private cached: Array<any>;
  private differ: IterableDiffer<Array<any>>;

  constructor(private differs: IterableDiffers) {
    this.differ = this.differs.find([]).create(null);
  }

  transform(array: Array<any>): Array<any> {
    // TODO: Throw an error if `array` isn't an Array
    if (Array.isArray(array) === false) return [];

    const changes = this.differ.diff(array);

    if (changes) this.cached = array.slice().reverse();

    return this.cached;
  }
}

I believe through several tutorials that it is correct to use an IterableDiffer for efficiency. But that isn't the topic of this question.

The fact that a constructor is required, I believe is the root of this simple test failing:

reverse.pipe.spec.ts

import { ReversePipe } from './reverse.pipe';

describe('ReversePipe', () => {
  it('create an instance', () => {
    const pipe = new ReversePipe();
    expect(pipe).toBeTruthy();
  });
});

The test fails with the error: TypeError: Cannot read property 'find' of undefined

This I (probably incorrectly) assume is because differs needs injecting in the test as the error message suggests that it is undefined.

Am I along the right lines and how should I write a simple test for the Pipe?

Update

I have tried to inject IterableDiffers into the Pipe being tested; while that has rectified the previous error I am not faced with a new one Error: Can't resolve all parameters for IterableDiffers: (?).

In the terminal, the error Cannot invoke an expression whose type lacks a call signature. Type 'IterableDiffers' has no compatible call signatures. is shown.

Both are describing the same problem just in different language.

My updated test is:

reverse.pipe.spec.ts

import { IterableDiffer, IterableDiffers } from '@angular/core';
import { TestBed, inject } from '@angular/core/testing';

import { ReversePipe } from './reverse.pipe';

describe('ReversePipe', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [IterableDiffers]
    });
  });

  // it('create an instance', () => {
  //   const array = [ 1, 2, 3 ];
  //   const pipe = new ReversePipe(new IterableDiffers);
  //   expect(pipe).toBeTruthy();
  // });

  it('create an instance', inject([IterableDiffers], (iterableDiffers: IterableDiffers) => {
    const array = [ 1, 2, 3 ];
    const pipe = new ReversePipe(iterableDiffers);

    expect(pipe).toBeTruthy();
  }));
});

Any and all help is very much appreciated.

like image 304
Jonathon Oates Avatar asked Jul 04 '17 16:07

Jonathon Oates


People also ask

How do you unit test a service with a dependency?

Service Dependencies As soon as you add even one dependency to your service, you need to also add it to your tests. In case of isolated tests, you will need to pass an instance of an injectable dependency class into the constructor of your service instantiation.

What can I use instead of TestBed?

TestBed. get() was deprecated as of Angular version 9. To help minimize breaking changes, Angular introduces a new function called TestBed. inject() , which you should use instead.


1 Answers

I was very nearly there with my updated test. You do not need to provide IterableDiffers:

reverse.pipe.spec.ts

import { IterableDiffers } from '@angular/core';
import { TestBed, inject } from '@angular/core/testing';

import { ReversePipe } from './reverse.pipe';

describe('ReversePipe', () => {
  it('should create an instance', inject([ IterableDiffers ], (iterableDiffers: IterableDiffers) => {
    const pipe = new ReversePipe(iterableDiffers);

    expect(pipe).toBeTruthy();
  }));

  it('should reverse the array of type Array<number>', inject([ IterableDiffers ], (iterableDiffers: IterableDiffers) => {
    const array = [ 1, 2, 3 ];
    const pipe = new ReversePipe(iterableDiffers);

    expect(pipe.transform(array)).toEqual([ 3, 2, 1 ]);
  }));

  it('should reverse the array of type Array<string>', inject([ IterableDiffers ], (iterableDiffers: IterableDiffers) => {
    const array = [ 'apple', 'banana', 'clementine' ];
    const pipe = new ReversePipe(iterableDiffers);

    expect(pipe.transform(array)).toEqual([ 'clementine', 'banana', 'apple' ]);
  }));
});

I also noticed I had an unnecessary if statement in reverse.pipe.spec.ts:

// TODO: Throw an error if `array` isn't an Array
if (Array.isArray(array) === false) return [];

The first argument of transform would always be an Array; of course, the TypeScript compiler would throw a TypeError if the argument was anything other than an Array.

For completeness my Pipe is:

reverse.pipe.ts

import {
  IterableDiffer,
  IterableDiffers,
  Pipe,
  PipeTransform
} from '@angular/core';

@Pipe({
  name: 'reverse',
  pure: false
})
export class ReversePipe implements PipeTransform {

  private cached: Array<any>;
  private differ: IterableDiffer<Array<any>>;

  constructor(private differs: IterableDiffers) {
    this.differ = this.differs.find([]).create(null);
  }

  public transform(array: Array<any>): Array<any> {
    const changes = this.differ.diff(array);

    if (changes) this.cached = array.slice().reverse();

    return this.cached;
  }
}
like image 168
Jonathon Oates Avatar answered Oct 21 '22 05:10

Jonathon Oates