How to mock pipes in Angular tests
A mock pipe in Angular tests can be created by MockPipe
function.
The second parameter of the function accepts a custom transform callback.
The mock pipe has the identical interface as its source pipe,
but all its methods are dummies.
To provide a mock pipe in a test, pass this source pipe into MockPipe
function.
TestBed.configureTestingModule({
declarations: [
// for a single pipe
MockPipe(Pipe),
// a fake transform callback
MockPipe(Pipe, value => JSON.stringify(value)),
// for a set of pipe
...MockPipes(Pipe1, Pipe2),
],
});
A mock pipe has:
- the same
name
- default transform is
() => undefined
to prevent problems with chaining - support for standalone pipes
Simple example
Let's imagine that in an Angular application TargetComponent
depends on DependencyPipe
pipe,
and we would like to replace it with its mock pipe.
Usually a test looks like:
describe('Test', () => {
let component: TargetComponent;
let fixture: ComponentFixture<TargetComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
// our component for testing
TargetComponent,
// the annoying dependency
DependencyPipe,
],
});
fixture = TestBed.createComponent(TargetComponent);
component = fixture.componentInstance;
});
});
To create a mock pipe out of a pipe, simply pass the original pipe into MockPipe
:
TestBed.configureTestingModule({
declarations: [
TargetComponent,
// profit
MockPipe(DependencyPipe, value => `mock:${value}`),
],
});
Or if we want to be like a pro, use MockBuilder
, its .mock
method
and call MockRender
:
describe('Test', () => {
beforeEach(() => {
return MockBuilder(TargetComponent, ItsModule)
// DependencyPipe is a declaration in ItsModule
.mock(DependencyPipe, value => `mock:${value}`);
});
it('should create', () => {
const fixture = MockRender(TargetComponent);
expect(fixture.point.componentInstance).toBeDefined();
expect(fixture.nativeElement.innerHTML).toContain('mock:foo');
// An instance of DependencyPipe from the fixture if we need it.
const pipe = ngMocks.findInstance(DependencyPipe);
expect(pipe).toBeDefined();
});
});
Standalone pipes
Since Angular 14, pipes can be implemented as a standalone declaration.
ng-mocks
detects and correctly mocks them.
To mock a standalone pipe, you need to call MockPipe
in imports:
TestBed.configureTestingModule({
imports: [
// for a single pipe
MockPipe(StandalonePipe),
],
declarations: [
// our component for testing
TargetComponent,
],
});
MockBuilder
also supports and correctly works with standalone pipes.
Advanced example
An advanced example of mocking pipes in Angular tests. Please, pay attention to comments in the code.
// A fake transform function.
const fakeTransform = (...args: string[]) => JSON.stringify(args);
describe('MockPipe', () => {
// A spy, just in case if we want to verify
// how the pipe has been called.
const spy = jasmine.createSpy().and.callFake(fakeTransform);
// in case of jest
// const spy = jest.fn().mockImplementation(fakeTransform);
beforeEach(() => {
return (
MockBuilder(TargetComponent, ItsModule)
// DependencyPipe is a declaration in ItsModule
.mock(DependencyPipe, spy)
);
});
it('transforms values to json', () => {
const fixture = MockRender(TargetComponent);
expect(fixture.nativeElement.innerHTML).toEqual(
'<target>["foo"]</target>',
);
// Also we can find an instance of the pipe in
// the fixture if it is needed.
const pipe = ngMocks.findInstance(DependencyPipe);
expect(pipe.transform).toHaveBeenCalledWith('foo');
expect(pipe.transform).toHaveBeenCalledTimes(1);
});
});