How to mock Pipe when testing Component
Asked Answered
A

9

68

Currently I am overriding providers to use mocked services like this:

beforeEach(inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
    tcb.overrideProviders(AddFieldToObjectDropdownComponent,
        [
             provide(ServiceA, { useClass: MockServiceA })),
             provide(ServiceB, { useClass: MockServiceB }))
        ])
    ...

I want to do same thing for pipes that the component uses. I tried, provide(PipeA, { useClass: MockPipeA }) and provide(PipeA, { useValue: new MockPipeA() }) but both didn't work.

Arroyo answered 2/9, 2016 at 13:9 Comment(6)
It's not promising when you haven't had any answers in 4 months. Did you ever figure this out?Matinee
@Matinee no unfortunately not it wasn't a high priority issue for me, but I will try it again soon with the new Test API, maybe there is a work around now.Arroyo
Any solution yet? I tried to not declare the original pipe and instead create a mock pipe and declare it. But somehow the pipe rendered result always show a empty string ''Swee
@Arroyo Do you only need the pipe in the template?Boastful
Yes, it is used in my component (template) only. @BoastfulArroyo
It's 2019 and still happening to me :( I was expecting the "provide" thing to work.Reseau
B
82

You can add your mockpipes in the declarations of the TestBed:

TestBed.configureTestingModule({
             declarations: [
                 AppComponent,
                 MockPipe
             ],
            ...

The MockPipe needs to have the @Pipe decorator with the original name.

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

@Pipe({name: 'pipename'})
class MockPipe implements PipeTransform {
    transform(value: number): number {
        //Do stuff here, if you want
        return value;
    }
}
Boastful answered 24/1, 2017 at 11:1 Comment(3)
Is there a way to spy on the mocked pipe using Jasmine? I'm trying to mock translate pipe and check it was called with the correct translation key.Garygarza
The Mock pipe should be the only pipe you register under that name. I mistakenly imported the Module the pipe name originated from. This prevented my Mock pipes from overriding their original implementation. If you're mocking a Pipe, your Mock should be the only instance that you register, don't include the original pipe registration.My
"don't include the original pipe registration" eesh, can be tough if the SUT's imports are out of your controlLixiviate
E
23

To stub the pipe, use Dinistro's answer. To spy on the pipe, you can complement that with the following:

let pipeSpy: jasmine.Spy;

beforeEach(() => {
    TestBed.configureTestingModule...

    pipeSpy = spyOn(MockPipe.prototype, 'transform');
};

it('should do whatever', () => {
    doYourStuff();

    expect(pipeSpy).toHaveBeenCalled();
}
Endpaper answered 13/7, 2017 at 13:12 Comment(1)
Also note: You can spyOn over the original pipe if you don't need MockPipeChartier
P
23

If you want reusable util function for mocking pipes, you can try this option:

export function mockPipe(options: Pipe): Pipe {
    const metadata: Pipe = {
      name: options.name
    };

    return <any>Pipe(metadata)(class MockPipe {});
}

And then just call this function inside the TestBed declarations array:

TestBed.configureTestingModule({
    declarations: [
        SomeComponent,
        mockPipe({ name: 'myPipe' }),
        mockPipe({ name: 'myOtherPipe' })
    ],
    // ...
}).compileComponents();
Pelorus answered 21/6, 2019 at 10:34 Comment(4)
This works for me, but can anyone explain this return statement in detail?Naranjo
@Naranjo the return statement is just another way of writing what Dinistro is doing in another response. The <any>Pipe() bit is decorated the subsequent class declaration. For what it's worth, here is a slight enhancement to shohrukh's response that allows mock piped data to be returned. It might help you visualize. ``` export function mockPipe(options: Pipe, mockReturn: any): Pipe { const metadata: Pipe = { name: options.name }; return <any>Pipe(metadata)( class MockPipe implements PipeTransform { public transform = () => mockReturn; } ); } ```Vilipend
I'm on angular 11 and this doesn't seem to work. They may have changed something internally. I get the error "TypeError: Cannot read property 'call' of undefined"Lenoir
@MattM use the code from Zack one comment above. But it would be cool if the original answer was also adjusted.Autophyte
A
12

One possibility is to use the ng-mocks library and use it like this:

TestBed.configureTestingModule({
  declarations: [
    TestedComponent,
    MockPipe(ActualPipe, (...args) => args[0]),
  ]
}).compileComponents();

The second argument to MockPipe defines what the transform function returns for an array of args.

Aphorize answered 18/10, 2019 at 12:42 Comment(0)
V
4

Mocking my pipe into simple class like

export class DateFormatPipeMock {
 transform() {
  return '29.06.2018 15:12';
 }
}

and simple use of useClass in my spec file

providers: [
  ...
  {provide: DateFormatPipe, useClass: DateFormatPipeMock}
  ...
]

worked for me :-)

Vaisya answered 19/7, 2018 at 9:45 Comment(1)
You may as well create a legit pipe and add it to your TestBed under declarations like you're supposed to.Lenoir
G
2

Building on top of @shohrukh 's answer, the following code gives you a reusable mock pipe that works in Angular 11/12:

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

export function mockPipe(name: string): Pipe {
  const metadata: Pipe = {
    name
  };

  return Pipe(metadata)(
    class MockPipe implements PipeTransform {
      transform() {}
    }
  );
}

Then use it in your test:

TestBed.configureTestingModule({
  declarations: [
    MyComponent,
    mockPipe('myPipe')
  ],
  ...
}).compileComponents();
Guerdon answered 30/7, 2021 at 10:55 Comment(0)
A
0

You can MockPipe from ng-mocks, or MockBuilder if you inject pipes as services.

MockPipe

beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [
      MockPipe(PipeA),
      MockPipe(PipeB, value => `transformed:${value}`),
    ],
  });
});

MockBuilder

beforeEach(() => {
  return MockBuilder(MyComponent, MyModule)
    .mock(PipeA)
    .mock(PipeB, value => `transformed:${value}`);
});
Anoa answered 15/4, 2022 at 19:58 Comment(0)
L
-1

You can use MockPipe npm package, but you need to import it like below.

import { MockPipe } from 'mock-pipe';

After that, all you need to do is to define your mock pipe in providers..

providers: [     
        {
            provide: HighlightPipe,
            useValue: MockPipe(HighlightPipe, () => 'mock')
        }
]

That's all.

Linton answered 13/5, 2019 at 9:26 Comment(1)
This may have been a good solution at one point, but the project is defunct and hasn't been updated in years. It doesn't work with Angular 11.Lenoir
W
-1

Often, we use pipes in templates. Here’s how you can mock a pipe. Note that the name of the pipe has to be the same as the pipe you are mocking.

@Pipe({ name: 'myPipe' })
class MyPipeMock implements PipeTransform {
  transform(param) {
    console.log('mocking');
    return true;
  }
}

You need to include the pipe when configuring your TestingModule if you are using it in a component’s template in the declarations.

Wivinah answered 4/10, 2019 at 7:26 Comment(1)
And how do you configure the test with this?Visionary

© 2022 - 2024 — McMap. All rights reserved.