Search code examples
angularjasmine

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?


Solution

  • 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 () => {
        await 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');
    });