Test pipe with dependencies on services
Solution 1
Because of the DI in your pipe, you need to configure a test environment (test bed) to resolve the dependency:
import { BrowserModule, DomSanitizer } from '@angular/platform-browser';
import { inject, TestBed } from '@angular/core/testing';
describe('SanitiseHtmlPipe', () => {
beforeEach(() => {
TestBed
.configureTestingModule({
imports: [
BrowserModule
]
});
});
it('create an instance', inject([DomSanitizer], (domSanitizer: DomSanitizer) => {
let pipe = new SanitiseHtmlPipe(domSanitizer);
expect(pipe).toBeTruthy();
}));
});
Solution 2
just in case anyone would like to reuse the constructor of the Pipe, you can use the TestBed to acheive the same result :
let pipe: SafeHtmlPipe;
let sanitized: DomSanitizer
beforeEach(async() => {
TestBed.configureTestingModule({
providers: [DomSanitizer]
});
sanitized = TestBed.get(DomSanitizer);
pipe = new SafeHtmlPipe(sanitized);
});
it('create an instance', () => {
expect(pipe).toBeTruthy();
});
Solution 3
In case you want to mock the whole providers and don't wanna use the constructor, this is how I do it (with Jest but replace the spy with your regular jasmine.createSpyObj)
spec
describe("MyPipe", () => {
let pipe: MyPipe;
const myServiceSpy = { myFunction: jest.fn() };
beforeEach(() => {
jest.clearAllMocks();
TestBed.configureTestingModule({
providers: [
MyPipe,
{
provide: MyService,
useValue: myServiceSpy
}
]
});
pipe = TestBed.inject(myPipe);
});
it("create an instance", () => {
expect(pipe).toBeTruthy();
});
});
pipe
@Pipe({
name: "myPipe"
})
export class MyPipe implements PipeTransform {
constructor(private readonly myService: MyService) {}
transform(value: Item): boolean {
// stuff with your service
return true;
}
}
![Ben Taliadoros](https://i.stack.imgur.com/oXmkz.jpg?s=256&g=1)
Ben Taliadoros
I am a Software Engineer working for a cyber security company in London. Answer my questions and I will send you sweets.
Updated on June 06, 2022Comments
-
Ben Taliadoros about 2 years
I have a pipe that sanatises HTML as below:
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer } from '@angular/platform-browser'; @Pipe({ name: 'sanitiseHtml' }) export class SanitiseHtmlPipe implements PipeTransform { constructor(private _sanitizer: DomSanitizer) {} transform(value: any): any { return this._sanitizer.bypassSecurityTrustHtml(value); } }
I want to test it as below:
describe('Pipe: Sanatiser', () => { let pipe: SanitiseHtmlPipe; beforeEach(() => { pipe = new SanitiseHtmlPipe(new DomSanitizer()); }); it('create an instance', () => { expect(pipe).toBeTruthy(); }); });
The DomSanatizer is an abstract class which is autowired by typescript by passing it into a constructor:
constructor(private _sanitizer: DomSanitizer) {}
Currently I get the typescript errror:
Cannot create an instance of the abstract class 'DomSanitizer'.
Does anyone know what typescript does when instantiating dependencies passed into a constructor in Angular? Or what the way to test something like this is?
-
Ben Taliadoros over 6 yearsI actually got the test running by commenting out the line: TestBed.resetTestEnvironment(); Remove this and I'll mark correct
-
Csaba Toth almost 6 yearsI also needed to add
import { BrowserModule, DomSanitizer } from '@angular/platform-browser';
-
codeepic over 5 years@Jota.Toledo What's the difference between
new SanitiseHtmlPipe(new DomSanitizer())
and injecting it like you did above. I am testing the pipe that has a custom service injected into it and I mocked my service and injected it into pipe like this:const pipe = new DataHealthPipe(new DateServiceMockBefore17thApril());
It is working for me, but I wonder whether this is the best approach. I had to mock the service 3 times, so each time itsgetFullDate()
method returns different date, for each test case. -
Jota.Toledo over 5 years@codeepic "What's the difference between new SanitiseHtmlPipe(new DomSanitizer()) and injecting it like you did above" its not possible to use new as the class is abstract
-
Jota.Toledo over 5 yearsDepending on the kind of dependencies that the SUT has, one can choose between real or mock objects. In the original OP case, mocking the
DomSanitizer
feels like an overkill, as the implementation is really straightforward. In your case, I cant tell if your service is complex enough to be worth mocking or not... -
codeepic over 5 years@Jota.Toledo - ok, you can't new up abstract classes, makes sense to use inject. In my case the
getFullDate()
method in mocked service relies on member variables of the class, some of them Observables that are set by user. I don't want to pull the whole gorilla and a tree if I want a banana - I only need the return object from the method, so mocking theDateService
class and returning different values fromDateService.getFullDate()
3 times for each test case seemed like lesser evil, even when I have to mock the class and its method 3 times. -
Jota.Toledo over 5 years@codeepic doesnt sound that complex. I dont know exactly what you mean by mock the class and its method 3 times, but my approach would be to provide a mock object and then spy with jasmine on the getFullDate() method and return what you need for your tests. Feel free to open a new question and tag me on it
-
codeepic over 5 years@Jota.Toledo Thanks for the tip with the
spy
andreturnValue
- I wasn't aware of that - I need to check Jasmine docs to see on what other goodies I am missing. I refactored my unit tests. -
martin-g over 4 yearsbut it seems
sanitized
is not usable:this.sanitizer.bypassSecurityTrustHtml is not a function
. This is the error when I try to use the pipe. -
Alain Boudard over 4 yearsWell, as far as I can tell, it's not a matter of method, this is because for whatever reason, this function and others are static, and therefore, you can't call them like that in your test. If I'm not wrong, you need to mock them like this if you want to test the methods calls : <pre><code>TestBed.configureTestingModule({ imports: [BrowserModule], providers: [ {provide: DomSanitizer, useValue: { sanitize: () => 'safeString', bypassSecurityTrustHtml: () => 'safeString' } } ] });</code></pre>
-
Alain Boudard over 4 yearssorry, abstract methods, not static
-
martin-g over 4 yearsIs there a way to inject the DomSanitizerImpl that is actually used in non-test code ?