Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make the component test to have a body or mock Renderer2

In one of our component, we use Renderer2 to add and remove some css classes/styles to the body element. To achieve that, we simply do something like:

this.renderer.setStyle(document.body, 'padding-left', '10px');
this.renderer.addClass(document.body, 'modal-container--opened');

As soon as we run the tests, we encounter errors:

Cannot read property 'add' of undefined

Cannot set property 'padding-left' of undefined

So it seems angular testBed doesn't create any body element.

In our test configuration, how to create a mocked body element? So we can run our tests against that element and see if style/class was properly applied by renderer.

It also seems mocking the Renderer2 is not possible.

We tried to create a spy:

let renderer: jasmine.SpyObj<Renderer2>;
renderer = jasmine.createSpyObj('renderer', ['addClass', 'removeClass', 'setStyle']);

then in TestBed.configureTestingModule (also tested in overrideProviders without more success):

{ provide: Renderer2, useValue: renderer }

But Angular ignores completely this override.

How to be able to test our component behavior, acting on document.body?

like image 519
BlackHoleGalaxy Avatar asked Feb 16 '18 12:02

BlackHoleGalaxy


People also ask

How to test component in Angular?

To run your tests using the Angular CLI, you use the ng test command in your terminal. As a result, Karma will open up the default browser and run all the tests written with the aid of Jasmine and will display the outcome of those tests.

How do you make a mock component?

A mock component in Angular tests can be created by MockComponent function. The mock component respects the interface of its original component, but all its methods are dummies. To create a mock component, simply pass its class into MockComponent function.

What is Angular fixture?

The Angular fixture provides the component's element directly through the fixture. nativeElement . content_copy const bannerElement: HTMLElement = fixture.

What is mock in Angular unit testing?

Mocking is the act of creating something that looks like the dependency but is something we control in our test. There are a few methods we can use to create mocks.


2 Answers

Instead of mocking the renderer try to hijack it. This should be working with Angular 6+.

In your component.spec.ts:

let renderer2: Renderer2;
...
beforeEach(async( () => {
    TestBed.configureTestingModule({
        ...
        providers: [Renderer2]
    }).compileComponents();
}));

beforeEach(() => {
    fixture = TestBed.createComponent(YourComponent);
    // grab the renderer
    renderer2 = fixture.componentRef.injector.get<Renderer2>(Renderer2 as Type<Renderer2>);
    // and spy on it
    spyOn(renderer2, 'addClass').and.callThrough();
    // or replace
    // spyOn(renderer2, 'addClass').and.callFake(..);
    // etc
});

it('should call renderer', () => {
    expect(renderer2.addClass).toHaveBeenCalledWith(jasmine.any(Object), 'css-class');
});
like image 180
GKA Avatar answered Jan 01 '23 05:01

GKA


Other approach would be, mocking service:

let renderer: MockRenderer;
class MockRenderer {
   addClass(document: string, cssClass: string): boolean {
     return true;
   }
}

beforeEach(async( () => {
   TestBed.configureTestingModule({
    ...
       providers: [{
              provide: Renderer2,
              useClass: MockRenderer
            }]
    }).compileComponents().then(() => {
        fixture = TestBed.createComponent(YourComponent);
        renderer =  fixture.debugElement.injector.get(Renderer2);
    });
}));

...
it('should call render', () => {
      spyOn(renderer, 'addClass');
      ...
      expect(renderer.addClass).toHaveBeenCalledWith(jasmine.any(Object), 'css-class');
});
like image 30
Jose Rojas Avatar answered Jan 01 '23 06:01

Jose Rojas