I am falling at the first hurdle testing an Angular Pipe that has a constructor.
My Pipe is as follows:
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:
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?
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:
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.
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.
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.
I was very nearly there with my updated test. You do not need to provide IterableDiffers
:
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:
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With